|
|
51CTO旗下网站
|
|
移动端

1.7.6 Lock自旋锁

《Java多线程与Socket:实战微服务框架》第1章多线程基础,本书尽量采用简要和通俗易懂的方式来介绍多线程知识。本节为大家介绍Lock自旋锁。

作者:庞永华来源:电子工业出版社|2019-03-26 15:59

【大咖·来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》

1.7.6 Lock自旋锁

java.util.concurrent.locks包中提供了锁(Lock)接口及其实现类(ReentrantLock)、读写分离锁(ReadWriteLock)接口及其实现类(ReentrantReadWriteLock)。与基于对象监视器的锁不同,线程在获取Lock锁的过程中不会被阻塞锁,而会通过循环不断地重试(自旋),直到当前持有该Lock锁的线程释放该锁。因此,Lock锁被形象地称为自旋锁。

ReentrantLock的基本用法示例:

  1. private static final Lock lock = new ReentrantLock();  
  2. public void run() {  
  3. lock.lock();  
  4. try {  
  5. // do something  
  6. } catch (InterruptedException e) {  
  7. e.printStackTrace();  
  8. } finally {  
  9. lock.unlock(); // 务必解锁  
  10. }  

ReentrantLock被称为重入锁,这种“锁”与synchronized关键字所依赖的基于对象监视器(monitor)的“锁”(内置锁)有以下不同:

. synchronized是关键字,由编译器负责生成加锁与解锁操作。因此,其运行时的加解锁动作是自动进行的。而ReentrantLock是一个类,需要在程序中调用其lock()方法等来加锁,并且一定要记得调用unlock()来解锁,而且***放在finally块中。

. ReentrantLock提供了4种上锁方式,可以有效地避免死锁。

. lock():上锁,如果不成功则阻塞,直到成功。在此期间不响应中断。

. tryLock():尝试以非阻塞方式上锁,如果不成功,则返回失败。

lock.tryLock(time, unit):尝试以阻塞方式在指定时间内上锁,超时则失败。

. lockInterruptly():上锁,如果不成功则阻塞,直到成功。在阻塞期间允许中断。

. 其通过Lock对象可以创建多个不同的条件变量(Condition),调用Condition对象的await()/signal()/signalAll()方法,可以更灵活多样地控制线程的等待和唤醒。而内置锁的唤醒只能是随机的,由线程调度决定。

. 其可以指定是公平锁还是非公平锁,而内置锁只能是非公平锁。

. 其可“重入”,即允许重复获取已持有的锁。内置锁则没有这种机制。

. 其提供丰富的获取“锁”对象及其相关Condition信息的方法,但不能通过线程转储(thread dump)来导出信息。而内置锁只能通过线程转储来获取信息。在这一点上,两者各有优劣。

ReentrantReadWriteLock类实现了读写分离锁,其内部定义了两个Lock接口的实现类(内部类):读锁(ReadLock)和写锁(WriteLock),并各创建了一个实例。在执行读操作时使用读锁,在执行写操作时使用写锁。其中ReadLock是共享锁,WriteLock是互斥锁。请结合下面这个读写分离锁示例来理解。

  1. public class C1_7_6 {  
  2. private static final Logger log = LoggerFactory.getLogger(C1_7_6.class);  
  3. private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();  
  4. private static Deque<Integer> data = new LinkedList<Integer>();  
  5. private static int value;  
  6. public static void main(String[] args) throws InterruptedException {  
  7. ExecutorService es = Executors.newCachedThreadPool();  
  8. for (int i = 0; i < 5; i++) {  
  9. es.execute(new Runnable() {  
  10. @Override  
  11. public void run() {  
  12. // 每个读线程向队列头添加10个数字  
  13. for (int i = 0; i < 10; i++) {  
  14. // 分别用readLock和writeLock的lock()方法试试  
  15. lock.writeLock().lock();  
  16. // lock.readLock().lock();  
  17. try {  
  18. data.addFirst(++value);  
  19. } finally {  
  20. // 分别用readLock和writeLock的unlock()方法试试  
  21. lock.writeLock().unlock();  
  22. // lock.readLock().unlock();  
  23. }  
  24. }  
  25. }  
  26. });  
  27. es.execute(new Runnable() {  
  28. @Override  
  29. public void run() {  
  30. // 每个读线程从队列尾读取10个数字  
  31. for (int i = 0; i < 10; i++) {  
  32. Integer v = null;  
  33. do {  
  34. // 分别用readLock和writeLock的lock()方法试试  
  35. lock.readLock().lock();  
  36. // lock.writeLock().lock();  
  37. try {  
  38. v = data.pollLast();  
  39. if (v == null) {  
  40. Thread.yield();  
  41. } else {  
  42. log.info("{}", v); // 试试把这句移到while之后  
  43. break;  
  44. }  
  45. } finally {  
  46. // 分别用readLock和writeLock的unlock()方法试试  
  47. lock.readLock().unlock();  
  48. // lock.writeLock().unlock();  
  49. }  
  50. } while (true);  
  51. // log.info("{}", v);  
  52. }  
  53. }  
  54. });  
  55. }  
  56. es.shutdown();  
  57. es.awaitTermination(10L, TimeUnit.SECONDS);  
  58. // log.info("----------Real order----------");  
  59. // for (Integer i : data) {  
  60. // log.info("{}", i);  
  61. //}  
  62. }  


喜欢的朋友可以加入官方的读书群

51CTO读书频道二维码


51CTO读书会第9群:808517103

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

16招轻松掌握PPT技巧

16招轻松掌握PPT技巧

GET职场加薪技能
共16章 | 晒书包

289人订阅学习

20个局域网建设改造案例

20个局域网建设改造案例

网络搭建技巧
共20章 | 捷哥CCIE

645人订阅学习

WOT2019全球人工智能技术峰会

WOT2019全球人工智能技术峰会

通用技术、应用领域、企业赋能三大章节,13大技术专场,60+国内外一线人工智能精英大咖站台,分享人工智能的平台工具、算法模型、语音视觉等技术主题,助力人工智能落地。
共50章 | WOT峰会

0人订阅学习

读 书 +更多

C#高级编程(第4版)

C#经典名著!2006年最受读者喜爱的十大技术开发类图书!也是Wrox红皮书中最畅销的品种之一,从第一版开始就名满天下;其第3版被中华读书报...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO播客