JDK1.5之前靠synchronized关键字保持同步,采用独占方式访问变量
乐观锁与悲观锁
独占锁是悲观锁,然而synchronized是独占锁(悲观的),它会导致其他需要锁的线程挂起,等待那个线程释放锁
乐观锁,每次不加锁而是假设没有冲突而去完成某项操作,冲突失败则重试,直至成功。
volatile
1.它保证变量修改,对于任何线程是可见的,
2.它保证 指令顺序不会重新排序,保证正确顺序下去,
当volatile修饰的变量修改时,它会将其他线程的这个变量变成无效状态,使得其他线程不得不向主存中访问变量。
java中的原子操作
原子操作:在一步之内就完成而且不能被中断。原子操作在多线程环境是安全的
对于long,double类型,并不是原子类型的操作。 对于64位操作系统,要分两步,一步写32位,一步写32位。
volatile本身不保证获取和设置操作是原子性,只保证修改的可见性。然而,java内存模型保证声明volatile的long和double变量的get和set操作是原子的。
例如: private volatile long foo;
AtomicInteger 内部成员private volatile int value;
synchronized是悲观的,先获取锁,再更新
原子变量更新是乐观的
CAS无锁算法
CAS , 是CPU指令,它是一种乐观锁技术,多个线程尝试更新同一个变量,其中一个线程更新这一个变量,其他线程操作实拍,失败线程不会挂起,而是被通知竞争失败,再次尝试。
CAS有3个操作数,内存值V,旧的预期值A,新的值B 如果 V==B那么更新值变为
//CAS比较与交换代码
do{
备份旧值
基于旧值构造新值(用一个新的变量存储新值)
}while(!CAS(内存值,旧值,新值))
public final long getAndAdd(long delta) {
while (true) {
long current = get();
long next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
//
V与A比较如果V没有被修改,那么替换新值,如果被修改了内存中的值返回false,并放弃刚才的操作,然后重新再次执行,直到执行成功
对CAS的支持 AtomicInt,AtomicLong.incrementAndGet()
自旋锁
自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。
public class SpinLock {
private AtomicReference<Thread> sign =new AtomicReference<>();
public void lock(){
Thread current = Thread.currentThread();
while(!sign .compareAndSet(null, current)){
}
}
public void unlock (){
Thread current = Thread.currentThread();
sign .compareAndSet(current, null);
}
}
使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。
当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。
由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。
CAS例子:非阻塞栈
do{
oldHead = head.get();
newHead.next = oldHead;
}while(!head.compareAndSet(oldHead,newHead));
首先从头中取出头元素,然后将新的头的next指向旧的元素(此时头是newHead),那么开始进行CAS算法,从内存中取出栈的头部和旧的头部进行比较,如果相同,进行更新,如果取出的栈头部与oldHead不同,那么撤销操作,循环不断的检测。