多线程编程中,当代码需要同步时我们会用到锁。Java为我们提供了内置锁(
)和显式锁(
- synchronized
)两种同步方式。显式锁是JDK1.5引入的,这两种锁有什么异同呢?是仅仅增加了一种选择还是另有其因?本文为您一探究竟。
- ReentrantLock
Java内置锁通过synchronized关键字使用,使用其修饰方法或者代码块,就能保证方法或者代码块以同步方式执行。使用起来非常近简单,就像下面这样:
- // synchronized关键字用法示例
- public synchronized void add(int t) { // 同步方法
- this.v += t;
- }
- public static synchronized void sub(int t) { // 同步静态方法
- value -= t;
- }
- public int decrementAndGet() {
- synchronized(obj) { // 同步代码块
- return--v;
- }
- }
这就是内置锁的全部用法,你已经学会了。
内置锁使用起来非常方便,不需要显式的获取和释放,任何一个对象都能作为一把内置锁。使用内置锁能够解决大部分的同步场景。“任何一个对象都能作为一把内置锁”也意味着出现synchronized关键字的地方,都有一个对象与之关联,具体说来:
内置锁这么好用,为什么还需多出一个显式锁呢?因为有些事情内置锁是做不了的,比如:
显式锁(ReentrantLock)正式为了解决这些灵活需求而生。ReentrantLock的字面意思是可重入锁,可重入的意思是线程可以同时多次请求同一把锁,而不会自己导致自己死锁。下面是内置锁和显式锁的区别:
提供了一种以定时结束等待的方式,如果线程在指定的时间内没有获得锁,该方法就会返回false并结束线程等待。
- RenentrantLock.tryLock(long timeout, TimeUnit unit)
给我们提供了一种以中断结束等待的方式。
- RenentrantLock.lockInterruptibly()
方法,显式锁通过
- Object.wait()
方法),进入等待状态的线程会挂起并自动释放锁,这些线程会被放入到条件队列当中。synchronized对应的只有一个条件队列,而ReentrantLock可以有多个条件队列,多个队列有什么好处呢?请往下看。
- Condition.await()
或者
- Object.notify()
方法唤醒,显式锁通过
- Object.notifyAll()
或者
- Condition.signal()
方法唤醒)。这就是多个条件队列的好处。
- Condition.signalAll()
使用内置锁时,对象本身既是一把锁又是一个条件队列;使用显式锁时,RenentrantLock的对象是锁,条件队列通过
方法获取,多次调用该方法可以得到多个条件队列。
- RenentrantLock.newCondition()
一个使用显式锁的典型示例如下:
- // 显式锁的使用示例
- ReentrantLock lock = new ReentrantLock();
- // 获取锁,这是跟synchronized关键字对应的用法。
- lock.lock();
- try {
- // your code
- } finally {
- lock.unlock();
- }
- // 可定时,超过指定时间为得到锁就放弃
- try {
- lock.tryLock(10, TimeUnit.SECONDS);
- try {
- // your code
- } finally {
- lock.unlock();
- }
- } catch(InterruptedException e1) {
- // exception handling
- }
- // 可中断,等待获取锁的过程中线程线程可被中断
- try {
- lock.lockInterruptibly();
- try {
- // your code
- } finally {
- lock.unlock();
- }
- } catch(InterruptedException e) {
- // exception handling
- }
- // 多个等待队列,具体参考[ArrayBlockingQueue](https://github.com/CarpenterLee/JCRecipes/blob/master/markdown/ArrayBlockingQueue.md)
- /** Condition for waiting takes */
- private final Condition notEmpty = lock.newCondition();
- /** Condition for waiting puts */
- private final Condition notFull = lock.newCondition();
注意,上述代码将
放在finally块里,这么做是必需的。显式锁不像内置锁那样会自动释放,使用显式锁一定要在finally块中手动释放,如果获取锁后由于异常的原因没有释放锁,那么这把锁将永远得不到释放!将unlock()放在finally块中,保证无论发生什么都能够正常释放。
- unlock()
内置锁能够解决大部分需要同步的场景,只有在需要额外灵活性是才需要考虑显式锁,比如可定时、可中断、多等待队列等特性。
显式锁虽然灵活,但是需要显式的申请和释放,并且释放一定要放到finally块中,否则可能会因为异常导致锁永远无法释放!这是显式锁最明显的缺点。
综上,当需要同步时请优先考虑更安全的更易用的隐式锁。
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html
来源: http://www.cnblogs.com/CarpenterLee/p/7896361.html