Day12多线程:
1.线程间通信-示例代码
2.线程间通信-解决安全问题
3.线程间通信-等待唤醒机制
4.线程间通信-代码优化
5.线程间通信-生产者消费者
1.线程间通信-示例代码
线程间通信:
其实就是多个线程在操作同一个资源,
但操作的动作不同。
示例代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 class Res
2 2 {
3 3 String name;
4 4 String sex;
5 5
6 6 }
7 7 class Input implements Runnable
8 8 {
9 9 int x=0;
10 10 private Res r;
11 11 public Input(Res r)
12 12 {
13 13 this.r=r;
14 14
15 15 }
16 16 public void run()
17 17 {
18 18 while(true)
19 19 {
20 20 if(x==0)
21 21 {
22 22 r.name="Mike";
23 23 r.sex="man";
24 24 }
25 25 else
26 26 {
27 27 r.name="黎黎";
28 28 r.sex="女女女";
29 29
30 30 }
31 31 x=(x+1)%2;
32 32
33 33 }
34 34
35 35
36 36 }
37 37 }
38 38 class Output implements Runnable
39 39 {
40 40 private Res r;
41 41 public Output(Res r)
42 42 {
43 43 this.r=r;
44 44 }
45 45 public void run()
46 46 {
47 47 while(true)
48 48 {
49 49 System.out.println(r.name+"---"+r.sex);
50 50
51 51 }
52 52
53 53 }
54 54 }
55 55 public class InputOutputDemo
56 56 {
57 57 public static void main(String[] args)
58 58 {
59 59 Res r=new Res();
60 60 Output out=new Output(r);
61 61 Input in=new Input(r);
62 62
63 63 Thread t1=new Thread(in);
64 64 Thread t2=new Thread(out);
65 65
66 66 t1.start();
67 67 t2.start();
68 68 }
69 69 }
运行:
出现了错误的输出。
2.线程间通信-解决安全问题
解决方法:
从两个方面考虑:
1.是否是多线程
2.是否用同一把锁。
改动后:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 class Res
2 {
3 String name;
4 String sex;
5
6 }
7 class Input implements Runnable
8 {
9 int x=0;
10 private Res r;
11 public Input(Res r)
12 {
13 this.r=r;
14
15 }
16 public void run()
17 {
18 while(true)
19 {
20 //添加同步代码块,使用对象r作为锁
21
22 synchronized(r)
23 {
24 if(x==0)
25 {
26 r.name="Mike";
27 r.sex="man";
28 }
29 else
30 {
31 r.name="黎黎";
32 r.sex="女女女";
33
34 }
35
36 }
37
38 x=(x+1)%2;
39
40 }
41
42
43 }
44 }
45 class Output implements Runnable
46 {
47 private Res r;
48 public Output(Res r)
49 {
50 this.r=r;
51 }
52 public void run()
53 {
54 while(true)
55 {
56 //添加同步代码块,使用对象r作为锁
57 synchronized(r)
58 {
59 System.out.println(r.name+"---"+r.sex);
60
61 }
62
63
64 }
65
66 }
67 }
68 public class InputOutputDemo
69 {
70 public static void main(String[] args)
71 {
72 Res r=new Res();
73 Output out=new Output(r);
74 Input in=new Input(r);
75
76 Thread t1=new Thread(in);
77 Thread t2=new Thread(out);
78
79 t1.start();
80 t2.start();
81 }
82 }
运行:
不再有错误的输出,但是输出显示大片大片的男,大片大片的女。
3.线程间通信-等待唤醒机制
要实现男女交替输出的效果,要用到wait和notify方法。
wait()
notify()
notifyAll()
都是用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中
只有同步才具有锁。
为什么这些操作线程的方法要定义在Object中呢?
因为这些方法在操作同步中的线程时,都必须标识它们所操作的线程持有的锁,
只有一个锁上的等待线程,可以被同一个锁上的notify唤醒。
不可以对不同锁上的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,而可以被任意对象调用的方法定义在Object中。
修改后:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 class Res
2 {
3 String name;
4 String sex;
5 //定义布尔型标志位
6 boolean flag=false;
7
8 }
9 class Input implements Runnable
10 {
11 int x=0;
12 private Res r;
13 public Input(Res r)
14 {
15 this.r=r;
16
17 }
18 public void run()
19 {
20 while(true)
21 {
22 synchronized(r)
23 {
24 //判断标志位,是否等待
25 if(r.flag)
26 {try{r.wait();}catch(Exception e){}}
27 if(x==0)
28 {
29 r.name="Mike";
30 r.sex="man";
31 }
32 else
33 {
34 r.name="黎黎";
35 r.sex="女女女";
36
37 }
38 //改变flag值
39 r.flag=true;
40 //唤醒
41 r.notify();
42
43 }
44
45 x=(x+1)%2;
46
47 }
48
49
50 }
51 }
52 class Output implements Runnable
53 {
54 private Res r;
55 public Output(Res r)
56 {
57 this.r=r;
58 }
59 public void run()
60 {
61 while(true)
62 {
63 synchronized(r)
64 {
65 //判断标志位,是否等待
66 if(!r.flag)
67 try{r.wait();}catch(Exception e){}
68 System.out.println(r.name+"---"+r.sex);
69 r.flag=false;
70 //唤醒
71 r.notify();
72
73 }
74
75
76 }
77
78 }
79 }
80 public class InputOutputDemo
81 {
82 public static void main(String[] args)
83 {
84 Res r=new Res();
85 Output out=new Output(r);
86 Input in=new Input(r);
87
88 Thread t1=new Thread(in);
89 Thread t2=new Thread(out);
90
91 t1.start();
92 t2.start();
93 }
94 }
运行:
4.线程间通信-代码优化
数据私有化,通过方法访问。
优化后代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 class Res
2 {
3 //数据成员设置为私有
4 private String name;
5 private String sex;
6 //定义布尔型标志位
7 private boolean flag=false;
8
9 //定义set、get同步方法
10 public synchronized void set(String name,String sex)
11 {
12 if(flag)
13 {try{this.wait();}catch(Exception e){}}
14 this.name=name;
15 this.sex=sex;
16
17 flag=true;
18 this.notify();
19 }
20 public synchronized void get()
21 {
22 if(!flag)
23 {try{this.wait();}catch(Exception e){}}
24 System.out.println(name+"---"+sex);
25
26 flag=false;
27 this.notify();
28 }
29
30 }
31 class Input implements Runnable
32 {
33 int x=0;
34 private Res r;
35 public Input(Res r)
36 {
37 this.r=r;
38
39 }
40 public void run()
41 {
42 while(true)
43 {
44 if(x==0)
45 {
46 //调用set方法
47 r.set("Mike","man");
48 }
49 else
50 {
51 r.set("黎黎","女女女");
52
53 }
54 x=(x+1)%2;
55 }
56
57
58 }
59 }
60 class Output implements Runnable
61 {
62 private Res r;
63 public Output(Res r)
64 {
65 this.r=r;
66 }
67 public void run()
68 {
69 while(true)
70 {
71 //调用get方法
72 r.get();
73 }
74
75 }
76 }
77 public class InputOutputDemo
78 {
79 public static void main(String[] args)
80 {
81 Res r=new Res();
82 //简化
83 new Thread(new Input(r)).start();
84 new Thread(new Output(r)).start();
85
86
87 }
88 }
5.线程间通信-生产者消费者
当有多个生产者,多个消费者时,必须循环判断标记,以避免产生生产2个,只消费了一个或类似的情况;
并且使用notifyAll方法,以避免出现所有线程都在等待的情况。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 class Resource
2 {
3 private String name;
4 private int count=1;
5 private boolean flag=false;
6
7 public synchronized void set(String _name)
8 {
9 while(flag)
10 {
11 try{wait();}catch(Exception e){}
12 }
13 name=_name+"--"+count++;
14 System.out.println(Thread.currentThread().getName()+"--生产者"+name);
15 flag=true;
16 notifyAll();
17
18 }
19 public synchronized void get()
20 {
21 while(!flag)
22 {
23 try{wait();}catch(Exception e){}
24 }
25 System.out.println(Thread.currentThread().getName()+"--------消费者"+name);
26 flag=false;
27 notifyAll();
28
29 }
30 }
31 class Producer implements Runnable
32 {
33 private Resource r=new Resource();
34 public Producer(Resource r)
35 {
36 this.r=r;
37 }
38 public void run()
39 {
40 while(true)
41 {
42 r.set("商品");
43 }
44 }
45 }
46 class Consumer implements Runnable
47 {
48 private Resource r=new Resource();
49 public Consumer(Resource r)
50 {
51 this.r=r;
52 }
53 public void run()
54 {
55 while(true)
56 {
57 r.get();
58 }
59 }
60 }
61 public class ProducerConsumerDemo
62 {
63 public static void main(String[] args)
64 {
65 Resource r=new Resource();
66
67 Producer pro=new Producer(r);
68 Consumer con=new Consumer(r);
69
70 Thread t1=new Thread(pro);
71 Thread t2=new Thread(pro);
72 Thread t3=new Thread(con);
73 Thread t4=new Thread(con);
74
75 t1.start();
76 t2.start();
77 t3.start();
78 t4.start();
79
80 }
81 }
运行: