线程通信
线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class ProducerConsumerDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
|
对于多个生产者和消费者。 为什么要定义 while 判断标记。
原因:让被唤醒的线程再一次判断标记。
为什么定义=notifyAll=, 因为需要唤醒对方线程。
因为只用 notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
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
|
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
// t1 t2
public synchronized void set(String name)
{
while(flag)
try{this.wait();}catch(Exception e){}//t1(放弃资格) t2(获取资格)
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
this.notifyAll();
}
// t3 t4
public synchronized void out()
{
while(!flag)
try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
flag = false;
this.notifyAll();
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.set("+商品+");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
|
进程同步优化解决方案
JDK1.5 中提供了多线程升级解决方案。
将同步 Synchronized 替换成显式的 Lock 操作。 将 Object 中的 wait,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
|
import java.util.concurrent.locks.*;
class ProducerConsumerDemo2
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
// t1 t2
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();//t1,t2
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
condition_con.signal();
}
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
}
// t3 t4
public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("+商品+");
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
|
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
|
class Res
{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name,String sex)
{
if(flag)
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name+"........"+sex);
flag = false;
this.notify();
}
}
class Input implements Runnable
{
private Res r ;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
if(x==0)
r.set("mike","man");
else
r.set("丽丽","女女女女女");
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r ;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class InputOutputDemo2
{
public static void main(String[] args)
{
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
/*
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
*/
}
}
|
等待的线程存放在线程池中 最先唤醒的是最先被冻结的线程
停止线程
stop 方法已经过时。
如何停止线程? 只有一种,run 方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让 run 方法结束,也就是线程结束。
特殊情况: 当线程处于了冻结状态。 就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread 类提供该方法 interrupt()
;
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
|
class StopThread implements Runnable
{
private boolean flag =true;
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0;
while(true)
{
if(num++ == 60)
{
//st.changeFlag();
//t1.interrupt();
//t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}
|
守护进程
join:
当 A 线程执行到了 B 线程的.join()方法时,A就会等待。等 B 线程都执行完,A才会执行。
join 可以用来临时加入线程执行。
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
|
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().toString()+"....."+x);
Thread.yield();
}
}
}
class JoinDemo
{
public static void main(String[] args) throws Exception
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
//t1.setPriority(Thread.MAX_PRIORITY);
t2.start();
//t1.join();
for(int x=0; x<80; x++)
{
//System.out.println("main....."+x);
}
System.out.println("over");
}
}
|
练习
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class MyThread extends Thread{
public void run(){
try {
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("MyThread running");
}
}
public class ThreadTest{
public static void main(String argv[]) {
MyThread t = new MyThread();
t.run();
t.start();
System.out.println("Thread Test");
}
}
|
代码分析过程:
MyThread t = new MyThread();
创建了一个线程。 t.run();
调用=MyThread=对象的=run()=方法。 这是只有一个线程在运行就是主线程。
当主线程执行到了=run()=方法中的=sleep(3000);=时。
这是主线程处于冻结状态。程序并没有任何执行。 当 3 秒过后,主线程打印了
=MyThread running=。 =run()=方法执行结束。
t.start();
开启了 t 线程。 有两种可能情况。 1. 第一种情况:
主线程在只执行了=t.start()=后,还具有执行权,继续往下执行,
打印了=Thread Test=。主线程结束。
t 线程获取执行权,调用自己的 run 方法。然后执行的=sleep(3000);=冻结 3 秒。
3 秒后,打印 MyThread running t 线程结束,整个程序结束。
- 第二种情况:
主线程执行到=t.start();=开启了 t 线程,t线程就直接获取到了执行权。
就调用自己的=run()=方法。
指定到=sleep(3000)=.t 线程冻结 3 秒,这是 t 线程就是释放了执行权。
那么主线程开始执行打印了=Thread Test=,主线程结束。
等到 3 秒后,t线程打印=MyThread running= ,然后 t 线程结束。 程序结束。
总结
线程间通信。
1
2
3
4
5
6
7
8
9
10
11
12
|
等待/唤醒机制。
也就是常见的生产者消费者问题。
1. 当多个生产者消费者出现时,
需要让获取执行权的线程判断标记。
通过while完成。
2. 需要将对方的线程唤醒。
仅仅用notify,是不可以的。因为有可能出现只唤醒本方。
有可能会导致,所有线程都等待。
所以可以通过notifyAll的形式来完成 。
|
这个程序有一个 bug。就是每次 notifyAll。都会唤醒本方。
可不可以只唤醒对方呢?
JDK1.5 版本提供了一些新的对象,优化了等待唤醒机制。
- 将 synchronized 替换成了 Lock 接口。 将隐式锁,升级成了显示锁。 Lock -
获取锁:lock(); -
释放锁:unlock();注意:释放的动作一定要执行,所以通常定义在 finally 中。 -
获取 Condition 对象:newCondition();
- 将 Object 中的 wait,notify,notifyAll 方法都替换成了 Condition 的 await,signal,signalAll。
和以前不同是:一个同步代码块具备一个锁,该所以具备自己的独立 wait 和 notify 方法。
现在是将 wait,notify 等方法,封装进一个特有的对象 Condition,而一个 Lock 锁上可以有多个 Condition 对象。
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
|
Lock lock = new ReentrantLock();
Condition conA = lock.newCondition();
Condition conB = lock.newCondition();
con.await();//生产,,消费
con.signal();生产
set()
{
if(flag)
conA.await();//生产者,
code......;
flag = true;
conB.signal();
}
out()
{
if(!flag)
conB.await();//消费者
code....;
flag = false;
conA.signal();
}
|
- wait 和 sleep 的区别: wait:释放 cpu 执行权,释放同步中锁。
sleep:释放 cpu 执行权,不释放同步中锁。
synchronized(锁) { wait(); }
停止线程:
1
2
3
4
5
6
7
8
|
**stop过时。**
原理:run方法结束。run方法中通常定义循环,指定控制住循环线程即可结束。
1. 定义结束标记。
2. 当线程处于了冻结状态,没有执行标记,程序一样无法结束。
这时可以循环,正常退出冻结状态,或者强制结束冻结状态。
强制结束冻结状态:interrupt();目的是线程强制从冻结状态恢复到运行状态。
但是会发生InterruptedException异常。
|
多线程重点: 1. 多线程的创建的两种方式,以及区别。
-
同步的特点。 同步的好处: 同步的弊端: 同步的前提:
同步的表现形式以及区别。 特例:static 同步函数锁是哪一个。
死锁代码要求写的出来。
-
线程间通信,看以上总结。
-
wait 和 sleep, yield()
: 临时暂停,可以让线程是释放执行权。
=yield()=方法 API:
`给调度程序的一个提示,当前线程愿意得到它当前的处理器的使用。调度程序可以自由地忽略这个提示。
产量是一个启发式的尝试,以改善线程之间的相对进展,否则将过度使用一个中央处理器。它的使用应结合详细的分析和基准,以确保它实际上有所需的效果。
使用这种方法是不恰当的。它可能是有用的调试或测试的目的,它可能有助于重现由于种族条件的错误。这也可能是有用的在设计并发控制结构如在 java.util.concurrent.locks 包的。
`