1、实现原理区别
Synchronized 是 JVM 实现的一种互斥同步锁,是 Java关键字,通过monitor对象来完成,被Synchronized修饰过的程序块,在编译前后被编译器生成了monitorenter和monitorexit两个字节码指令。在虚拟机执行到monitorenter指令时,首先要尝试获取对象的锁:如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1;当执行monitorexit指令时,将锁计数器-1;当计数器为0时,锁就被释放了。如果获取对象失败了,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。Java中Synchronize通过在对象头设置标志,达到了获取锁和释放锁的目的。synchronized 的实现涉及到锁的升级,具体为无锁、偏向锁、自旋锁、向OS申请重量级锁
ReentrantLock是Lock的实现类,是一个互斥的同步锁。ReentrantLock是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。ReentrantLock实现则是通过利用CAS(CompareAndSwap)自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能。
2、释放时机
Synchronized的获取和释放锁由JVM实现,用户不需要显示的释放锁,非常方便。
ReentrantLock则需要用户去手动释放锁,如果没有手动释放锁,就可能导致死锁现象。一般通过lock()和unlock()方法配合try/finally语句块来完成,使用释放更加灵活。
3、中断情况
Synchronized 是不可中断类型的锁,除非加锁的代码中出现异常或正常执行完成。如果获取锁的线程进入休眠或者阻塞,除非当前线程异常,否则其他线程尝试获取锁必须一直等待ReentrantLock则可以中断,可通过trylock(long timeout,TimeUnit unit)设置超时方法或者将lockInterruptibly()放到代码块中,调用interrupt方法进行中断,如此可以避免死锁的出现。
4、是否公平锁
公平锁多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁。Synchronized 为非公平锁,每当锁被释放后,任何一个线程都有机会竞争到锁,这样做的目的是为了提高执行性能,缺点是可能会产生线程饥饿现象。
ReentrantLock则即可以选公平锁也可以选非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁,但公平锁表现的性能不是很好。
5、是否可以绑定条件
Synchronized不能绑定条件。ReentrantLock通过绑定Condition结合await()/singal()方法实现线程的精确唤醒,而不是像synchronized通过Object类的wait()/notify()/notifyAll()方法要么随机唤醒一个线程要么唤醒全部线程。
6、锁的对象
Synchronzied锁的是对象,锁是保存在对象头里面的,根据对象头数据来标识是否有线程获得锁/争抢锁;
ReentrantLock锁的是线程,根据进入的线程和int类型的state标识锁的获得/争抢。
7、性能
Synchronized早期实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大。但是在Java6中对其进行了非常多的改进。
在竞争不激烈时:Synchronized的性能要优于 ReetrantLock;
高竞争情况下:Synchronized的性能会下降几十倍,但是 ReetrantLock的性能能维持常态。