Lock应用之 线程协作

简介:

内部锁(对象监视器)使用Object类的wait(), notify(), notifyAll()方法来进行线程之间的协作通信;Lock锁引入Condition来支持线程之间通信协作,Condition提供await(), signal(), signalAll()方法实现与内部锁同样的等待与唤醒功能,但与内部锁不同的是一个Lock可以绑定多个Condition,以满足不同条件下唤醒不同线程的功能。


最典型的线程协作例子就是生产者与消费者。如果使用内部锁控制线程通信,所有线程,不管生产者还是消费者,都被同一个对象监视,当新生产的对象放入消费队列后,生产者会唤醒所有的线程,包括其它生产者,这时队列可能已经满了,所以被唤醒的生产者只好继续回头进入等待队列,一直轮询直到唤醒一个消费者才会进行消费,然后继续。因为Lock支持绑定多个Condition,所以如果使用Lock实现,可以为当队列有对象可消费创建一个Condition,当队列有空位创建一个Condition,这样,生产者和消费者可以相互明确告知,而不是像内部锁广播的方式而缺乏明确的目标对象。


简而言之,内部锁的notify()与notifyAll()会唤醒任何一个线程,不管线程的等待条件是否已经满足;Condition则提供了针对不同类型线程定制唤醒条件的实现,减少无谓的唤醒。


另外,经过测试,在多核情况下,一个线程还可能被一些其它的条件唤醒,所以测试结果并不一定会满意,有待细究。


内部锁与Lock方式实现的生产者与消费者代码案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import  java.util.LinkedList;
import  java.util.Queue;
import  java.util.concurrent.TimeUnit;
import  java.util.concurrent.locks.Condition;
import  java.util.concurrent.locks.Lock;
import  java.util.concurrent.locks.ReentrantLock;
public  class  TestLockCondition {
     private  static  final  int  MAX_SIZE =  3 ;
     private  static  final  int  PRODUCER_NUM =  5 ;
     private  static  final  int  CONSUMER_NUM =  1 ;
     private  Queue<Object> queue =  new  LinkedList<Object>();
     private  volatile  boolean  isWorkingTime =  true ;
     private  volatile  boolean  isUsingLock =  true ;
     private  Lock lock =  new  ReentrantLock();
     private  Condition full = lock.newCondition();  
     private  Condition empty = lock.newCondition();
       
     private  void  produceWithLock()  throws  InterruptedException{
         lock.lock();
         try {
             while (queue.size() >= MAX_SIZE){
                 System.out.println(Thread.currentThread().getName() +  " go wait." );
                 full.await();
                 System.out.println(Thread.currentThread().getName() +  " was awaken." );
             }
                       
             queue.offer( new  Object());
             System.out.println(Thread.currentThread().getName() +  " produce one object." );
             empty.signalAll();
             //empty.signal();
         }
         finally {
             lock.unlock();
         }              
     }
       
     private  void  consumeWithLock()  throws  InterruptedException{
         lock.lock();
         try {
             while (queue.isEmpty()){
                 System.out.println(Thread.currentThread().getName() +  " go wait." );
                 empty.await();
                 System.out.println(Thread.currentThread().getName() +  " was awaken." );
             }
                       
             queue.poll();
             System.out.println(Thread.currentThread().getName() +  " consume one object." );
             full.signalAll();
             //full.signal();
         }
         finally {
             lock.unlock();
         }              
     }
       
     private  synchronized  void  produce()  throws  InterruptedException{
         while (queue.size() >= MAX_SIZE){
             System.out.println(Thread.currentThread().getName() +  " go wait." );
             wait();
             System.out.println(Thread.currentThread().getName() +  " was awaken." );
         }
                   
         queue.offer( new  Object());
         System.out.println(Thread.currentThread().getName() +  " produce one object." );
         //notifyAll();
         notify();
     }
       
     private  synchronized  void  consume()  throws  InterruptedException{
         while (queue.isEmpty()){
             System.out.println(Thread.currentThread().getName() +  " go wait." );
             wait();
             System.out.println(Thread.currentThread().getName() +  " was awaken." );
         }
                   
         queue.poll();
         System.out.println(Thread.currentThread().getName() +  " consume one object." );
         //notifyAll();
         notify();
     }
       
     public  static  void  main(String[] args)  throws  InterruptedException {
         TestLockCondition testLockCondition =  new  TestLockCondition();
           
         Thread[] producers =  new  Thread[PRODUCER_NUM];
           
         for ( int  i= 0 ; i<PRODUCER_NUM; i++){
             producers[i] =  new  Thread(testLockCondition. new  Producer());
             producers[i].start();
         }      
           
         Thread[] consumers =  new  Thread[CONSUMER_NUM];
         for ( int  i= 0 ; i<CONSUMER_NUM; i++){
             consumers[i] =  new  Thread(testLockCondition. new  Consumer());
             consumers[i].start();
         }
           
         TimeUnit.SECONDS.sleep( 10 );
           
         for ( int  i= 0 ; i<PRODUCER_NUM; i++){
             System.out.println(producers[i].getName() +  " : "  + producers[i].getState());
             producers[i].interrupt();
         }
           
         for ( int  i= 0 ; i<CONSUMER_NUM; i++){
             System.out.println(consumers[i].getName() +  " : "  + consumers[i].getState());
             consumers[i].interrupt();
         }
                   
         testLockCondition.isWorkingTime =  false ;
     }
       
     private  class  Consumer  implements  Runnable{
         @Override
         public  void  run() {
             try  {
                 while (isWorkingTime){
                     if (isUsingLock){
                         //System.out.println(Thread.currentThread().getName() + " is using Lock.");
                         consumeWithLock();
                     }
                     else {
                         consume();
                     }                  
                 }              
             catch  (InterruptedException e) {
                 e.printStackTrace();
             }          
         }
           
     }
     private  class  Producer  implements  Runnable{
         @Override
         public  void  run() {
             try  {
                 while (isWorkingTime){
                     if (isUsingLock){
                         //System.out.println(Thread.currentThread().getName() + " is using Lock.");
                         produceWithLock();
                     }
                     else {
                         produce();
                     }
                 }              
             catch  (InterruptedException e) {
                 e.printStackTrace();
             }
         }
           
     }
}






     本文转自sarchitect 51CTO博客,原文链接http://blog.51cto.com/stevex/1300223,如需转载请自行联系原作者


相关文章
|
2月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
254 6
|
1月前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
51 2
|
2月前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
2月前
|
存储 监控 安全
深入理解ThreadLocal:线程局部变量的机制与应用
在Java的多线程编程中,`ThreadLocal`变量提供了一种线程安全的解决方案,允许每个线程拥有自己的变量副本,从而避免了线程间的数据竞争。本文将深入探讨`ThreadLocal`的工作原理、使用方法以及在实际开发中的应用场景。
103 2
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
85 7
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
2月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
53 4
|
3月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
3月前
|
监控 Java
在实际应用中选择线程异常捕获方法的考量
【10月更文挑战第15天】选择最适合的线程异常捕获方法需要综合考虑多种因素。没有一种方法是绝对最优的,需要根据具体情况进行权衡和选择。在实际应用中,还需要不断地实践和总结经验,以提高异常处理的效果和程序的稳定性。
45 3
|
3月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
82 4