synchronized关键字与JUC中Lock的区别,CAS操作及ABA问题

synchronized

synchronized关键字在javac编译之后会在同步块的前后分别形成monitorenter,monitorexit两个字节码指令。都需要reference对象来指明锁定和解锁的对象,如果没有明确指定,根据synchronized修饰的方法类型(实例方法或者类方法),来决定取代码所在的对象实例还是取类型对应的Class对象来作为线程要持有的锁。

在执行monitorenter指令时,首先要尝试获取对象的锁,如果对象没有被锁定,或者当前线程已经持有了那个对象的锁,就把锁的计数器的值加一,而在执行monitorexit指令时,是对计数器的值减一。计数器的值为零时,锁被释放。如果获取对象锁失败,那当前线程就应当被阻塞等待,直到请求锁定的对象被持有它的线程释放为止。

关于synchronized的直接推论

  1. 被synchronized修饰的同步块对同一条线程来说是可重入的。同一线程反复进入同步块也不会出现死锁情况。
  2. 被synchronized修饰的同步块在持有锁的线程执行完毕并释放锁之前,会无条件的阻塞后面的其他线程的进入。无法强制让其释放锁,也无法中断等待锁的线程。

JUC

JDK5之后,Java类库中新提供了 java.util.concurrent 包(J.U.C), 其中的java.util.locks.Lock接口成为了Java另一种互斥同步手段。
空重入锁(ReentrantLock)是Lock接口常见的一个实现类,它比synchronized增加了一些功能:

  1. 等待可中断:当持有锁的线程长时间不释放时,正在等待的线程可选择放弃等待。
  2. 公平锁:多个线程在等待同一个锁的时候,必须按照申请锁的时间的先后来依次获得锁;非公平锁:任何一个在等待的线程都有机会占用锁。ReentrantLock默认为非公平锁,通过构造函数可以转为公平锁(boolean值),使用了公平锁会使ReentrantLock的性能下降,影响吞吐量。
  3. 锁绑定多个条件:一个ReentrantLock对象可以同时绑定多个Condition对象。

JDK6中对synchronized进行了优化后synchronized与ReentrantLock的性能持平

sychronized与Lock的不同

  • synchronized是在Java语法层面的同步,Lock是个接口。
  • Lock需要自己手动释放锁,不然会造成死锁,而synchronized会自动释放
  • Java虚拟机可以在线程和对象的元数据中记录synchronized中锁的相关信息,而Lock很难得知。

CAS 保证操作的原子性

CAS(Compare and Swap):CAS指令需要三个操作数,分别是内存地址V,旧的预期值A,和准备设置的新值B。
CAS指令执行时,当且仅当V符合A时,处理器才会用B更新V的值,否则它就不执行更新,执行期间不会被其他线程打断。

CAS漏洞:ABA问题

ABA问题:检测V是否符合A时,A可能被其他线程所修改(从A改为B然后从B再改回A),但是CAS不能发现,还是会继续执行。大部分情况下,ABA问题不会影响程序并发的正确性。

ABA问题的解决

如果变量的值只能朝着一个方向转换,比如从A到B,B到C,不构成环形,就不会出现ABA问题。JDK中的AtomicStampedReference类给每个变量的状态值都配置了一个时间戳,从而避免了ABA问题的产生。

 private static AtomicStampedReference atoStaRef = new AtomicStampedReference(100, 0);
 Thread threadC = new Thread(()->{
            try
            {
                Thread.sleep(1000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }  
             // 每次操作时间戳加一         			          	            
            atoStaRef.compareAndSet(100,101,atoStaRef.getStamp(),atoStaRef.getStamp()+1);
            atoStaRef.compareAndSet(101,100,atoStaRef.getStamp(),atoStaRef.getStamp()+1);
        });

        Thread threadD = new Thread(()->{
        // 获得最初版本的时间戳
            int stamp = atoStaRef.getStamp();
            try
            {
                Thread.sleep(2000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            boolean flag = atoStaRef.compareAndSet(100,101,stamp,stamp+1);
            System.out.println(flag);
        });
        threadC.start();
        threadD.start();
结果为false

AtomicInteger 没有解决ABA问题例子

private static AtomicInteger atomicInteger = new AtomicInteger(100);
    public static void main(String[] args) throws InterruptedException
    {
        Thread threadA = new Thread(()->{
            atomicInteger.compareAndSet(100,101);
            atomicInteger.compareAndSet(101,100);
        });

        Thread threadB = new Thread(()->{
            try
            {
                Thread.sleep(1000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            boolean flag = atomicInteger.compareAndSet(100,101);
            System.out.println(flag);
        });

        threadA.start();
        threadB.start();
        threadA.join();
        threadB.join();
    }
    结果为:true
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值