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问题不会影响程序并发的正确性。

猜你喜欢

转载自blog.csdn.net/weixin_43663421/article/details/109351067