他们之间的关系
从三个角度来剖析二者之间的区别
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class MySignal{
protected boolean hasDataToProcess = false;
public synchronized boolean hasDataToProcess(){
return this.hasDataToProcess;
}
public synchronized void setHasDataToProcess(boolean hasData){
this.hasDataToProcess = hasData;
}
}
|
假设系统中有两条线程,这两条线程分别代表取钱者和存钱者。现在系统有一种特殊的要求,系统要求存款者和取钱者不断的实现存款和取钱动作,而且要求每当存款者将钱存入指定账户后,取钱者立即将钱取走.不允许存款者两次存钱,也不允许取钱者两次取钱。
我们通过设置一个旗标来标识账户中是否已有存款,有就为true,没有就标为false。具体代码如下:
首先我们定义一个Account类,这个类中有取钱和存钱的两个方法,由于这两个方法可能需要并发的执行取钱、存钱操作,所有将这两个方法都修改为同步方法.(使用synchronized关键字)。
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
|
public class Account {
private String accountNo;
private double balance;
//标识账户中是否有存款的旗标
private boolean flag=false;
public Account() {
super();
}
public Account(String accountNo, double balance) {
super();
this.accountNo = accountNo;
this.balance = balance;
}
public synchronized void draw (double drawAmount){
try {
if(!flag){
this.wait();
}else {
//取钱
System.out.println(Thread.currentThread().getName()+" 取钱:"+drawAmount);
balance=balance-drawAmount;
System.out.println("余额 : "+balance);
//将标识账户是否已有存款的标志设为false
flag=false;
//唤醒其它线程
this.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void deposit(double depositAmount){
try {
if(flag){
this.wait();
}
else{
System.out.println(Thread.currentThread().getName()+"存钱"+depositAmount);
balance=balance+depositAmount;
System.out.println("账户余额为:"+balance);
flag=true;
//唤醒其它线程
this.notifyAll();
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class DrawThread implements Runnable {
private Account account;
private double drawAmount;
public DrawThread(Account account, double drawAmount) {
super();
this.account = account;
this.drawAmount = drawAmount;
}
public void run() {
for(int i=0;i<100;i++){
account.draw(drawAmount);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class depositThread implements Runnable{
private Account account;
private double depositAmount;
public depositThread(Account account, double depositAmount) {
super();
this.account = account;
this.depositAmount = depositAmount;
}
public void run() {
for(int i=0;i<100;i++){
account.deposit(depositAmount);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
|
public class TestDraw {
public static void main(String[] args) {
//创建一个账户
Account account=new Account();
new Thread(new DrawThread(account, 800),"取钱者").start();
new Thread(new depositThread(account, 800),"存款者甲").start();
new Thread(new depositThread(account, 800),"存款者乙").start();
new Thread(new depositThread(account, 800),"存款者丙").start();
}
}
|
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
|
存款者甲存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
存款者丙存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
存款者甲存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
存款者丙存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
存款者甲存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
存款者丙存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
存款者甲存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
存款者丙存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
存款者甲存钱800.0
账户余额为:800.0
取钱者 取钱:800.0
余额 : 0.0
|
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
|
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
//显示定义Lock对象
private final Lock lock=new ReentrantLock();
//获得指定Lock对象对应的条件变量
private final Condition con=lock.newCondition();
private String accountNo;
private double balance;
//标识账户中是否有存款的旗标
private boolean flag=false;
public Account() {
super();
}
public Account(String accountNo, double balance) {
super();
this.accountNo = accountNo;
this.balance = balance;
}
public void draw (double drawAmount){
//加锁
lock.lock();
try {
if(!flag){
// this.wait();
con.await();
}else {
//取钱
System.out.println(Thread.currentThread().getName()+" 取钱:"+drawAmount);
balance=balance-drawAmount;
System.out.println("余额 : "+balance);
//将标识账户是否已有存款的标志设为false
flag=false;
//唤醒其它线程
// this.notifyAll();
con.signalAll();
}
} catch (Exception e) {
e.printStackTrace();
}
finally{
lock.unlock();
}
}
public void deposit(double depositAmount){
//加锁
lock.lock();
try {
if(flag){
// this.wait();
con.await();
}
else{
System.out.println(Thread.currentThread().getName()+"存钱"+depositAmount);
balance=balance+depositAmount;
System.out.println("账户余额为:"+balance);
flag=true;
//唤醒其它线程
// this.notifyAll();
con.signalAll();
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
|
管道流是JAVA中线程通讯的常用方式之一,基本流程如下:
创建管道输出流PipedOutputStream pos和管道输入流PipedInputStream pis
将pos和pis匹配,pos.connect(pis);
将pos赋给信息输入线程,pis赋给信息获取线程,就可以实现线程间的通讯了
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
|
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class testPipeConnection {
public static void main(String[] args) {
/**
* 创建管道输出流
*/
PipedOutputStream pos = new PipedOutputStream();
/**
* 创建管道输入流
*/
PipedInputStream pis = new PipedInputStream();
try {
/**
* 将管道输入流与输出流连接 此过程也可通过重载的构造函数来实现
*/
pos.connect(pis);
} catch (IOException e) {
e.printStackTrace();
}
/**
* 创建生产者线程
*/
Producer p = new Producer(pos);
/**
* 创建消费者线程
*/
Consumer1 c1 = new Consumer1(pis);
/**
* 启动线程
*/
p.start();
c1.start();
}
}
/**
* 生产者线程(与一个管道输入流相关联)
*
*/
class Producer extends Thread {
private PipedOutputStream pos;
public Producer(PipedOutputStream pos) {
this.pos = pos;
}
public void run() {
int i = 0;
try {
while(true)
{
this.sleep(3000);
pos.write(i);
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 消费者线程(与一个管道输入流相关联)
*
*/
class Consumer1 extends Thread {
private PipedInputStream pis;
public Consumer1(PipedInputStream pis) {
this.pis = pis;
}
public void run() {
try {
while(true)
{
System.out.println("consumer1:"+pis.read());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
|
consumer1:0
consumer1:1
consumer1:2
consumer1:3
......
管道流虽然使用起来方便,但是也有一些缺点
管道流只能在两个线程之间传递数据 。线程consumer1和consumer2同时从pis中read数据,当线程producer往管道流中写入一段数据后,每一个时刻只有一个线程能获取到数据,并不是两个线程都能获取到producer发送来的数据,因此一个管道流只能用于两个线程间的通讯。不仅仅是管道流,其他IO方式都是一对一传输。
管道流只能实现单向发送,如果要两个线程之间互通讯,则需要两个管道流 。可以看到上面的例子中,线程producer通过管道流向线程consumer发送数据,如果线程consumer想给线程producer发送数据,则需要新建另一个管道流pos1和pis1,将pos1赋给consumer1,将pis1赋给producer,具体例子本文不再多说。
转自:https://www.tuhd.top/2017/08/04/2017-08-04-threadandprocess/
原文:https://www.cnblogs.com/itplay/p/11375584.html