JUC(4): 深入理解CAS

深入理解CAS

CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的旧值,那么则执行操作!如果不是就一直循环!(自旋锁

  • CAS是一种系统原语,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。

缺点:

1、 循环会耗时

2、一次性只能保证一个共享变量的原子性

3、ABA问题

乐观锁是一种思想,CAS只是这种思想的一种实现方式。底层用Unsafe类实现:==Unsafe相当于一个后门 ,基于该类可以直接操作特定内存的数据==

并发包中的原子操作类AtomicInteger来看下,如何在不使用锁的情况下保证线程安全,主要看下getAndIncrement方法,相当于i++的操作:

public class AtomicInteger extends Number implements java.io.Serializable {  
    private volatile int value; 
	public final int get() {  
        return value;  
    }  

    public final int getAndIncrement() {  
        for (;;) {  
            int current = get();  
            int next = current + 1;  
            if (compareAndSet(current, next))  		//CAS实现
                return current;  
        }  
    }  

    public final boolean compareAndSet(int expect, int update) {  
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  	//底层用Unsafe类调用本地C++方法操作内存
    }  
}

解决ABA问题:

线程1准备用CAS修改变量值A,在此之前,其它线程将变量的值由A替换为B,又由B替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了。

解决办法

  • 原子引用:AtomicStampedReference / AtomicMarkableReference

  • (乐观锁)引入原子引用,给值加一个修改版本号,每次值变化,都会修改它版本号,CAS操作时都对比此版本号

private static AtomicStampedReference<Integer> atomicStampedRef =
     new AtomicStampedReference<>(1, 0);
public static void main(String[] args){
 Thread main = new Thread(() -> {
     System.out.println("操作线程" + Thread.currentThread() +",初始值 a = " + atomicStampedRef.getReference());
     int stamp = atomicStampedRef.getStamp(); //获取当前标识别
     try {
         Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
     //此时expectedReference未发生改变,但是stamp已经被修改了,所以CAS失败
     boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1);  
     System.out.println("操作线程" + Thread.currentThread() +",CAS操作结果: " + isCASSuccess);
 },"主操作线程");

 Thread other = new Thread(() -> {
     Thread.yield(); // 确保thread-main 优先执行
	    atomicStampedRef.compareAndSet(1,2,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
     System.out.println("操作线程" + Thread.currentThread() +",【increment】 ,值 = "+ 			atomicStampedRef.getReference());
     atomicStampedRef.compareAndSet(2,1,atomicStampedRef.getStamp(),atomicStampedRef.getStamp() +1);
     System.out.println("操作线程" + Thread.currentThread() +",【decrement】 ,值 = "+ atomicStampedRef.getReference());},"干扰线程");

 main.start();
 other.start();
}

* 自己实现一个自旋锁

利用AtomicReference 中的cas操作,实现自旋锁

public class SpinLockDemo{
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
	public void Lock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"\t"+"-----come in");
        while(!atomicReference.compareAndSet(null,thread)){		//用这个循环实现自旋,如果是空的,那我们把thread放进去
        
        }
    }   

    public void UnLock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);//把当前线程踢出去,置为null
        System.out.println(Thread.currentThread().getName()+"\t"+"-------task over,unLock.....");
    }

    public static void main(String[] args){
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            spinLockDemo.Lock();
            try { TimeUnit.SECONDS.sleep( 5 ); } catch (InterruptedException e) { e.printStackTrace(); }
            spinLockDemo.UnLock();
        },"A").start();

        //暂停一会儿线程,保证A线程先于B线程启动并完成
        try { TimeUnit.MILLISECONDS.sleep( 500); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            spinLockDemo.Lock();//B  -----come in  B只是尝试去抢锁,但是一直在自旋。

            spinLockDemo.UnLock();//A结束后 B立马抢到锁,然后马上结束了
        },"B").start();
    }
}
//A  -----come in
//B  -----come in
//A  -------task over,unLock.....
//B  -------task over,unLock.....
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值