首先,在jvm中有一个main memory,而每个线程都有自己的working memory,一个线程对一个variable进行操作的时候,会先在自己的working memory里面建立一个copy,操作完成之后再写入main memory,如果有多个线程同时操作同一个variable,就可能会出现不可预知的结果,所以线程安全就是为了避免这种情况的发生。在java中,确保线程安全的方法有两种:一种是使用内置锁,一种是使用原子类(java.util.concurrent包下的);
对于内置锁:
可以同步方法,也可以同步代码块,不过建议同步代码块,因为同步方法会锁住整个对象,哪怕这个类中有多个不相关的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。
对于原子类:
锁机制会存在以下问题:
1、在多线程竞争下,加锁、释放锁都会导致比较多的上下文切换(即存储和恢复cpu的状态的过程)和调度延时,引起性能问题;
2、一个线程持有锁会导致其它所有需要此锁的线程挂起;
3、如果一个优先级高的线程等待一个优先级线程低的线程释放锁会导致优先级倒置,引起性能风险;
对于原子类,它所用的机制就是CAS机制(Compare And Swap),CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B,当且仅当预期值A与内存值V相同时,才会将内存值V修改为B,否则什么都不做;
现代cpu提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而compareAndSet()就是用这些代替了锁定,compareAndeSet()是调用本地方法来完成cpu指令的操作。
来看看AutomicInteger的源码:
private volatile int value;//毫无疑问,没有锁的机制下,必须借助volatile保证线程间的数据可见性(volatile的原理是使线程直接读取该变量并且不去缓存它,就确保了线程读取到的变量是同一内存中的,保证一致性)
public final int get(){
return value;
}
来看看++i是怎么实现的:
public final int increamentAndGet(){
for(;;){
int current = get();
int next = current + 1;
if(compareAndSet(current,next))
return next;
}
}