Day12多线程:
1.线程间通信-示例代码
2.线程间通信-解决安全问题
3.线程间通信-等待唤醒机制
4.线程间通信-代码优化
5.线程间通信-生产者消费者
1.线程间通信-示例代码
线程间通信:
其实就是多个线程在操作同一个资源,
但操作的动作不同。
示例代码:
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.是否用同一把锁。
改动后:
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中。
修改后:
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.线程间通信-代码优化
数据私有化,通过方法访问。
优化后代码:
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方法,以避免出现所有线程都在等待的情况。
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 }
运行: