Synchronized和ReentrantLock区别
相似点:
都是加锁方式同步,并且都是阻塞式的同步,即一个线程获取了对象锁,进入同步块,其他访问该同步块的线程必须阻塞在同步块的外面等待,而线程进行阻塞和唤醒的代价是比较高的,
功能区别:
synchornized是Java语言的关键字,是原生语法层面的互斥,需要JVM实现,而ReentrantLock是JDK1.5以后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句来完成。
便利性:
很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
锁的细粒度和灵活度:很明显ReenTrantLock优于Synchronized
性能的区别:
在Synchornized优化以前,synchornized的性能比ReenTrantLock差很多的。但是在synchronized引入偏向锁、轻量级锁后,两者性能就差不多了,在两种方法都可用的情况下,官方建议使用synchornized。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。
synchornized
synchornized经过编译,会在同步块的前后形成monitor-enter和monitor-exit这两个字节码指令。在执行monitor-enter指令时。首先要尝试获取对象锁,如果对象没被锁定,或者当前的线程已经拥有对象锁,把锁的计数器加1,相应的,在执行monitor指令执行时会将计数器-1,如果计数器为0,锁就被释放。如果获取对象失败,那么当前线程就要进行阻塞,直到对象锁被另一个线程释放。
ReentrantLock
相对于synchornized,提供了一些高级功能,主要用以下三点。
1.等待可中断,只有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,相当于synchornized来说可以避免出现死锁的情况。通过lock.lockinterruptibly()来实现这个机制。
2.公平锁,多个线程等待同一个锁的时候,必须按照申请锁的时间顺序获得所,synchornized锁非公平锁,ReentrantLock默认的构造函数是创建爱你的非公平锁,可以通过参数true()设为公平锁,但公平锁的性能不太好。
3.一个ReentrantLock对象可以同时绑定多个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
简单来说,ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。想尽办法避免线程进入内核的阻塞状态是我们去分析和理解锁设计的关键钥匙