1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyAll()控制线程通信。不灵活。
2.利用Condition控制线程通信,灵活。
3.利用管道pipe进行线程通信,不推荐
4.利用BlockingQueue控制线程通信
本文就讲解利用Condition控制线程通信,非常灵活的方式。
Condition类是用来保持Lock对象的协调调用。
对Lock不了解的可以参考:Java线程同步Lock同步锁代码示例
Condition介绍
使用Condition可以让那些已经得到lock对象却无法继续执行的线程释放lock对象,Condition对象也可以唤醒处于等待的线程。
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,使用其 newCondition() 方法。
Condition类提供了如下三个方法:
await():造成当前线程在接到信号或被中断之前一直处于等待状态。 该方法流程:
1.新建Condition Node包装线程,加入Condition队列。
2.释放当前线程占有的锁
3.阻塞当前线程
signal():唤醒当前lock对象的一个等待线程。signal方法只是将Node(await方法封装的)修改了状态,并没有唤醒线程。要将修改状态后的Node唤醒,一种是再次调用await(),一种是调用unlock()。//这局句很重要,不明白的可以看我下一篇博客。
signalAll():唤醒当前lock对象的所有等待线程。只有当前线程放弃对lock的锁定,被唤醒的线程才可以执行。
代码实例:
代码逻辑:Account类实现同步的取钱(draw)、存钱(deposit)操作;DrawThread循环取钱的线程、DepositThread循环存钱的线程。
Account:
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
|
package condition; import java.util.concurrent.locks.*; /** *存钱、取钱 */ public class Account { //显示定义Lock对象 private final Lock lock = new ReentrantLock(); //可重入锁 //获得指定Lock对象对应的条件变量 private final Condition cond = lock.newCondition(); //获得condition实例 private String accountNo; private double balance; //标识账户中是否已经存款的旗标 private boolean flag = false ; public Account(){} public Account(String accountNo , double balance) { this .accountNo = accountNo; this .balance = balance; } public void setAccountNo(String accountNo) { this .accountNo = accountNo; } public String getAccountNo() { return this .accountNo; } public double getBalance() { return this .balance; } /** *取款 * @param drawAmount */ public void draw( double drawAmount) { //加锁 lock.lock(); System.out.println(Thread.currentThread().getName() + "进入封锁区。。。。。。。。" ); try { //如果账户中还没有存入存款,该线程等待 if (!flag) { cond.await(); } else { //执行取钱操作 System.out.println(Thread.currentThread().getName() + " 取钱:" + drawAmount); balance -= drawAmount; System.out.println( "账户余额为:" + balance); //将标识是否成功存入存款的旗标设为false flag = false ; //唤醒该Lock对象对应的其他线程 cond.signalAll(); } } catch (InterruptedException ex) { ex.printStackTrace(); } //使用finally块来确保释放锁 finally { lock.unlock(); System.out.println( "释放了" ); } } /** * 存款 * @param depositAmount */ public void deposit( double depositAmount) { lock.lock(); System.out.println(Thread.currentThread().getName() + "进入封锁区。。。。。。。。" ); try { //如果账户中已经存入了存款,该线程等待 if (flag) { System.out.println(Thread.currentThread().getName() + "等待。。。。。。" ); cond.await(); } else { //执行存款操作 System.out.println(Thread.currentThread().getName() + " 存款:" + depositAmount); balance += depositAmount; System.out.println( "账户余额为:" + balance); //将标识是否成功存入存款的旗标设为true flag = true ; //唤醒该Lock对象对应的其他线程 cond.signalAll(); } } catch (InterruptedException ex) { ex.printStackTrace(); } //使用finally块来确保释放锁 finally { lock.unlock(); System.out.println(Thread.currentThread().getName() + "释放锁。。。。" ); } } public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if (obj != null && obj.getClass() == Account. class ) { Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false ; } } |
DrawThread:
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
|
package condition; /** *取钱 */ public class DrawThread extends Thread { //模拟用户账户 private Account account; //当前取钱线程所希望取的钱数 private double drawAmount; public DrawThread(String name , Account account , double drawAmount) { super (name); this .account = account; this .drawAmount = drawAmount; } //当多条线程修改同一个共享数据时,将涉及到数据安全问题。 public void run() { for ( int i = 0 ; i < 6 ; i++ ) { account.draw(drawAmount); } } } |
DepositThread:
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
|
package condition; /** *存钱 */ public class DepositThread extends Thread { //模拟用户账户 private Account account; //当前取钱线程所希望取的钱数 private double depositAmount; public DepositThread(String name , Account account , double depositAmount) { super (name); this .account = account; this .depositAmount = depositAmount; } //当多条线程修改同一个共享数据时,将涉及到数据安全问题。 public void run() { for ( int i = 0 ; i < 2 ; i++ ) { account.deposit(depositAmount); System.out.println(Thread.currentThread().getName()+ " 存钱结束!" ); } } } |
TestDraw:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package condition; public class TestDraw { public static void main(String[] args) { //创建一个账户 Account acct = new Account( "1234567" , 0 ); new DrawThread( "取钱者" , acct , 800 ).start(); new DepositThread( "存钱者甲" , acct , 800 ).start(); new DepositThread( "存钱者乙" , acct , 800 ).start(); new DepositThread( "存钱者丙" , acct , 800 ).start(); } } |
运行结果:
取钱者进入封锁区。。。。。。。。
存钱者甲进入封锁区。。。。。。。。
存钱者甲 存款:800.0
账户余额为:800.0
存钱者甲释放锁。。。。
存钱者丙进入封锁区。。。。。。。。
存钱者甲 存钱结束!
存钱者丙等待。。。。。。
存钱者乙进入封锁区。。。。。。。。
存钱者乙等待。。。。。。
释放了
存钱者甲进入封锁区。。。。。。。。
存钱者甲等待。。。。。。
取钱者进入封锁区。。。。。。。。
取钱者 取钱:800.0
账户余额为:0.0
释放了
取钱者进入封锁区。。。。。。。。
这里结果只粘贴了一部分。。。。聪明的你会发现这个程序最后阻塞啦,注意是阻塞不是死锁!阻塞的原因是:三个存钱的线程都运行结束了,但是取钱的线程还没有,所以阻塞啦。
总结
以上就是本文关于Java编程中实现Condition控制线程通信的全部内容,希望对大家有所帮助。有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!
原文链接:http://www.cnblogs.com/jycboy/p/5623210.html