[关闭]
@kiraSally 2018-03-12T18:57:09.000000Z 字数 5952 阅读 2756

并发番@Lock接口一文通(1.8版)

JAVA 并发 源码 1.8版


1.内部锁的不足

  • 不可中断:使用内部锁(指的是synchronized)时,不能中断正在等待获取锁的线程
  • 不可超时:使用内部锁时,在请求锁失败情况下,必须无限等待,没有超时效果
  • 自动释放:使用内部锁时,内部锁必须在获取它们的代码块中被自动释放(虽然对代码来说是种简化且对异常友好)
  • 不可伸缩:使用内部锁时,无法细粒度控制锁(伸缩性不足),即无法实现锁分离和锁联结,比如为每个链表节点(或部分)加锁从而允许不同的线程能够独立操作链表的不同节点(部分),遍历或修改链表时,需先获取该节点锁并直到获取下一个节点锁时才释放当前节点锁
  • 性能问题:使用内部锁时,在有竞争情况下仍会出现性能问题,尽管JDK6对内部锁进行了优化,但无论是偏向锁或是轻量级锁都是针对无竞争情况的优化,无竞争情况下与ReentractLock性能一致,但有竞争时Lock明显更高效

2.Lock接口综述

  • 定义: JDK1.5引入Lock接口,其定义了一些抽象的锁操作,相比synchronized,Lock提供了无条件、可轮询、可定时、可中断的锁获取操作,所有加锁和解锁的方法都是显式的
  • 实现: Lock的实现必须提供具有与synchronized相同的内存语义,但加锁的语义、调度算法、顺序保证、性能特性可以有所不同
  • 使用: Lock接口的实现基本是通过聚合一个同步器AbstractQueuedSynchronized的子类来完成线程的访问控制
  • 对比内部锁: Lock缺少隐式获取/释放锁的便捷,但却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种内部锁不具备的同步性,甚至还支持读写锁分离,同时允许获取和释放可以不在同一个块中
  • 补充:此番为 并发番@AQS一文通(赶制中)的子番,同时也是 并发番@ReentractLock一文通(赶制中)的预备番

3.Lock接口方法标准使用

  1. //标准用法
  2. Lock lock = new ReentrantLock();
  3. //获取锁应在try之前,因为若获取锁时发生异常,异常抛出同时会导致锁无故释放
  4. lock.lock();
  5. try{
  6. doSometing();
  7. }finally{
  8. //注意:必须在finally块中释放锁,目的是保证在获取到锁之后,最终能被释放
  9. lock.unlock();
  10. }

4.Lock接口重点方法

4.1 lock方法

  • lock方法应具有与内部锁加锁相同的内存语义,即无锁阻塞和支持可重入
  • lock方法必须搭配unlock方法使用,同时必须在finally中显式调用unlock方法释放锁
  1. /**
  2. * Acquires the lock.
  3. * 获取锁,调用该方法的当前线程将会获取锁,当锁获得后,从该方法返回
  4. * <p>If the lock is not available then the current thread becomes
  5. * disabled for thread scheduling purposes and lies dormant until the
  6. * lock has been acquired.
  7. * 若当前锁不可用(已被占有),当前线程会一直休眠直到锁为可被获取状态
  8. * <p><b>Implementation Considerations</b>
  9. * 实现该方法的注意事项
  10. * <p>A {@code Lock} implementation may be able to detect erroneous use
  11. * of the lock, such as an invocation that would cause deadlock, and
  12. * may throw an (unchecked) exception in such circumstances. The
  13. * circumstances and the exception type must be documented by that
  14. * {@code Lock} implementation.
  15. * 该方法的实现需要能发现lock被错误使用,如死锁或抛出不可查异常(即可运行期异常和Error)
  16. * 此时该实现必须用文档注明其可能出现的异常或需要的使用环境
  17. */
  18. void lock();

4.2 lockInterruptibly方法

  • lockInterruptibly方法提供可中断的锁获取操作并允许在可取消的活动中使用
  1. public boolean doTask throws InterruptedException(){
  2. lock.lockInterruptibly();
  3. try{
  4. return cancelTask();
  5. }finally{
  6. lock.unlock();
  7. }
  8. }
  9. //取消任务
  10. private boolean cancelTask() throws InterruptedException {...}
  1. /**
  2. * Acquires the lock unless the current thread is
  3. * {@linkplain Thread#interrupt interrupted}.
  4. * 可中断地获取锁,即在锁的获取中可以中断当前线程
  5. * <p>Acquires the lock if it is available and returns immediately.
  6. * 当获取锁时锁可用就立即返回
  7. * <p>If the lock is not available then the current thread becomes
  8. * disabled for thread scheduling purposes and lies dormant until
  9. * one of two things happens:
  10. * <ul>
  11. * <li>The lock is acquired by the current thread; or
  12. * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
  13. * current thread, and interruption of lock acquisition is supported.
  14. * </ul>
  15. * 若当前锁不可用(已被占有),当前线程会一直休眠直到以下两种情况发生:
  16. * 1.锁被当前线程获取
  17. * 2.其他线程中断当前线程,同时锁的获取允许被中断
  18. * <p><b>Implementation Considerations</b>
  19. * 实现该方法的注意事项
  20. * <p>The ability to interrupt a lock acquisition in some
  21. * implementations may not be possible, and if possible may be an
  22. * expensive operation. The programmer should be aware that this
  23. * may be the case. An implementation should document when this is
  24. * the case.
  25. * 该方法属于拓展方法,只有需要中断服务的时候才需要实现它
  26. * <p>An implementation can favor responding to an interrupt over
  27. * normal method return.
  28. * 相对于返回,该方法更适合抛出一个中断响应,比如中断异常
  29. * @throws InterruptedException if the current thread is
  30. * interrupted while acquiring the lock (and interruption
  31. * of lock acquisition is supported)
  32. */
  33. void lockInterruptibly() throws InterruptedException;

4.3 tryLock方法

  • tryLock方法提供可定时与可轮询的锁获取方式,与无条件的锁获取相比,具有更完善的错误恢复机制
  • tryLock方法能够有效的防止死锁的发生,比如使用轮询锁优雅失败规避死锁
  • tryLock方法同时提供定时锁的功能,其允许在限时活动内部使用独占锁,当线程获取锁、被中断或超时后返回
  • tryLock方法支持轮询获取锁:通过一个循环配合tryLock()来不断尝试获取锁,由于tryLock()非阻塞因此会立即返回是否成功获取锁的结果;当不能获取所有的锁时,应释放已获得的所有锁并重新尝试获取
  • tryLock方法同时支持响应中断
  1. /**
  2. * Acquires the lock only if it is free at the time of invocation.
  3. * 尝试非阻塞的获取锁,调用该方法后立即返回是否成功获取锁true/false
  4. * <p>Acquires the lock if it is available and returns immediately
  5. * with the value {@code true}.
  6. * If the lock is not available then this method will return
  7. * immediately with the value {@code false}.
  8. * 当锁不可用时立即返回false
  9. * This usage ensures that the lock is unlocked if it was acquired, and
  10. * doesn't try to unlock if the lock was not acquired.
  11. * 该实现应确保当锁被获取时是未锁状态,当未被获取时不会尝试解锁
  12. * @return {@code true} if the lock was acquired and
  13. * {@code false} otherwise
  14. */
  15. boolean tryLock();
  16. /**
  17. * Acquires the lock if it is free within the given waiting time and the
  18. * current thread has not been {@linkplain Thread#interrupt interrupted}.
  19. * 没有被中断当前线程在指定超时时间内获取锁
  20. * If the lock is not available then the current thread becomes disabled for
  21. * thread scheduling purposes and lies dormant until one of three things happens:
  22. * <ul>
  23. * <li>The lock is acquired by the current thread; or
  24. * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
  25. * current thread, and interruption of lock acquisition is supported; or
  26. * <li>The specified waiting time elapses
  27. * </ul>
  28. * <p>If the specified waiting time elapses then the value {@code false} is returned.
  29. * If the time is less than or equal to zero, the method will not wait at all.
  30. * 当前线程在以下三种情况下会返回:
  31. * 1.当前线程在超时时间内获得锁
  32. * 2.当前线程在超时时间内被中断
  33. * 3.超时时间结束,返回false,线程不再被阻塞
  34. * @param time the maximum time to wait for the lock
  35. * @param unit the time unit of the {@code time} argument
  36. * @return {@code true} if the lock was acquired and {@code false}
  37. * if the waiting time elapsed before the lock was acquired
  38. * @throws InterruptedException if the current thread is interrupted
  39. * while acquiring the lock (and interruption of lock
  40. * acquisition is supported)
  41. */
  42. boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

4.4 unlock方法

  • 使用lock方法、lockInterruptibly方法、tryLock方法都必须显式调用unlock方法释放锁
  • unlock方法必须在finally块中执行,这也是Lock使用的一个代码隐患(容易忘记执行)
  • unlock方法允许与lock方法不在同一个块(即{})中执行,但业务代码必须保证在try-finally块中执行
  1. /**
  2. * Releases the lock.
  3. * 释放锁
  4. */
  5. void unlock();

4.5 newCondition方法

  • Lock搭配Condition可以实现更加灵活的锁获取与释放的条件控制
  1. /**
  2. * Returns a new {@link Condition} instance that is bound to this
  3. * {@code Lock} instance.
  4. * 返回一个等待通知组(条)件
  5. * <p>Before waiting on the condition the lock must be held by the current thread.
  6. * A call to {@link Condition#await()} will atomically release the lock
  7. * before waiting and re-acquire the lock before the wait returns.
  8. * 该组件与当前锁绑定,当先线程只有获得锁才能调用该组件的await方法并释放锁
  9. * @return A new {@link Condition} instance for this {@code Lock} instance
  10. * @throws UnsupportedOperationException if this {@code Lock}
  11. * implementation does not support conditions
  12. */
  13. Condition newCondition();

并发番@Lock接口一文通(1.8版)黄志鹏kira 创作,采用 知识共享 署名-非商业性使用 4.0 国际 许可协议 进行许可。

本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注