相同之处
都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)。
不同之处
实现层次
这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。
锁的获取
synchronized:假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待。
ReentrantLock 获取锁的方式更加灵活
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁。
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false。
c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁,就返回true,如果等待超时,返回false。
d) lockInterruptibly:如果获取了锁立即返回,如果没有获取锁,当前线程处于休眠状态,直到或者获取锁,或者当前线程被别的线程中断。
锁的释放
synchronized:自动释放锁,同步块代码执行完毕或出现异常。
ReentrantLock :finally块中释放锁
相比于synchronized,ReentrantLock增加了一些高级功能。
等待可中断
等待可中断是指持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
公平锁
公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序获得锁。非公平锁无法保证这一点,在锁被释放时,任何一个等待的线程都有机会获得锁。
synchronized中的锁是非公平锁,ReentrantLock默认是非公平锁,可以通过构造函数的布尔参数设为公平锁。
绑定多个条件
一个ReentrantLock对象可以同时绑定多个Condition对象,而在synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件,如果要和多余一个条件关联的时候,就不得不额外地添加一个锁,而ReentrantLock则无须这么做,只需要多次调用new Condition()方法即可。
总结如下
类别 |
synchronized |
Lock |
---|---|---|
存在层次 |
Java的关键字,在jvm层面上 |
是一个类,API层面 |
锁的释放 |
执行完同步块代码,释放锁 执行同步块代码发生异常,释放锁 |
在finally中释放锁 |
锁的获取 |
假设A线程获得锁,B线程等待。 如果A线程阻塞,B线程会一直等待 |
Lock有多个锁获取的方式 |
锁状态 |
无法判断 |
可以判断 |
锁类型 |
可重入 不可中断 非公平 |
可重入 可中断 可公平 |