多线程_线程间通信
2018-06-18 02:36:20来源:未知 阅读 ()
线程间通信:
多个线程在处理同一资源,但是他们的任务不同(一部分线程生产鸭子,另一部分线程销售鸭子)
从下面的代码开始,一步步的引出问题并解决
1 public class Text2 { 2 public static void main(String[] args) { 3 Ziyuan r=new Ziyuan(); //创建资源 4 Inp a=new Inp(r); //创建输入任务 5 Outp b=new Outp(r); //创建输出任务 6 Thread t=new Thread(a); //创建输入线程,执行路径(执行输入任务) 7 Thread t1=new Thread(b); //创建输出线程,执行路径(执行输出任务) 8 t.start(); //开启线程 9 t1.start(); 10 } 11 } 12 class Ziyuan{ 13 String name; 14 String sex; 15 } 16 class Inp implements Runnable{ 17 Ziyuan r; 18 int a=0; 19 Inp(Ziyuan r){ 20 this.r=r; 21 } 22 public void run(){ 23 while(true){ 24 if(a==0){ 25 r.name="黑"; 26 r.sex="男"; 27 }else{ 28 r.name="白白"; 29 r.sex="女女"; 30 } 31 a=(a+1)%2; 32 } 33 } 34 } 35 class Outp implements Runnable{ 36 Ziyuan r; 37 int a=0; 38 Outp(Ziyuan r){ 39 this.r=r; 40 } 41 public void run(){ 42 while(true) 43 System.out.println(r.name+"...."+r.sex); 44 } 45 }
输出的结果会出现这种情况:
黑....女女
白白....男
会出现这种情况是因为有多个线程操作共享资源,并且操作共享资源的代码有多条,可以用同步解决这种安全问题
修改后的代码为:(就是简单的加上一个锁就可以解决问题,要保证线程使用的是同一个锁)
1 public class Text2 { 2 public static void main(String[] args) { 3 Ziyuan r=new Ziyuan(); //创建资源 4 Inp a=new Inp(r); //创建输入任务 5 Outp b=new Outp(r); //创建输出任务 6 Thread t=new Thread(a); //创建输入线程,执行路径(执行输入任务) 7 Thread t1=new Thread(b); //创建输出线程,执行路径(执行输出任务) 8 t.start(); //开启线程 9 t1.start(); 10 } 11 } 12 class Ziyuan{ 13 String name; 14 String sex; 15 } 16 class Inp implements Runnable{ 17 Ziyuan r; 18 int a=0; 19 Inp(Ziyuan r){ 20 this.r=r; 21 } 22 public void run(){ 23 while(true){ 24 synchronized(r){ 25 if(a==0){ 26 r.name="黑"; 27 r.sex="男"; 28 }else{ 29 r.name="白白"; 30 r.sex="女女"; 31 } 32 a=(a+1)%2; 33 } 34 } 35 } 36 } 37 class Outp implements Runnable{ 38 Ziyuan r; 39 int a=0; 40 Outp(Ziyuan r){ 41 this.r=r; 42 } 43 public void run(){ 44 while(true) 45 synchronized(r){ 46 System.out.println(r.name+"...."+r.sex); 47 } 48 } 49 }
这样虽然解决了安全问题,但是输入(输出)都是成片存在的,假设我们是在卖票,这样就会出现一张票被重复卖出多次。
现在我就想,输入一个,打印一个。这样就引出了等待唤醒机制。
等待唤醒机制:
涉及的方法:
wait() 让线程进入冻结状态, 把线程存储在线程池中
notify() 唤醒线程池中的一个线程
notifyall() 唤醒线程池中所有线程
这些方法都是操做线程状态的
这些方法必须定义在同步中
这些方法必须明确属于哪个锁
因为这些方法是监视器的方法,同步中一把锁只能有一组监视器,锁是任意的对象,任意的对象调用的方式一定定义在Object类,所以这些方法定义在Object类中。(我的理解,锁调用这些方法去改变线程状态,要保证锁一定能调用这些方法,那就只能把这些方法定义在Object中)
1 public class Text2 { 2 public static void main(String[] args) { 3 Ziyuan r=new Ziyuan(); //创建资源 4 Inp a=new Inp(r); //创建输入任务 5 Outp b=new Outp(r); //创建输出任务 6 Thread t=new Thread(a); //创建输入线程,执行路径(执行输入任务) 7 Thread t1=new Thread(b); //创建输出线程,执行路径(执行输出任务) 8 t.start(); //开启线程 9 t1.start(); 10 } 11 } 12 class Ziyuan{ 13 String name; 14 String sex; 15 boolean bool=false; 16 } 17 class Inp implements Runnable{ 18 Ziyuan r; 19 int a=0; 20 Inp(Ziyuan r){ 21 this.r=r; 22 } 23 public void run(){ 24 while(true){ 25 synchronized(r){ 26 if(r.bool){ 27 try {r.wait();} catch (InterruptedException e) {} 28 } 29 if(a==0){ 30 r.name="黑"; 31 r.sex="男"; 32 }else{ 33 r.name="白白"; 34 r.sex="女女"; 35 } 36 a=(a+1)%2; 37 r.bool=true; 38 r.notify(); 39 } 40 } 41 } 42 } 43 class Outp implements Runnable{ 44 Ziyuan r; 45 int a=0; 46 Outp(Ziyuan r){ 47 this.r=r; 48 } 49 public void run(){ 50 while(true) 51 synchronized(r){ 52 if(!r.bool){ 53 try {r.wait();} catch (InterruptedException e) {} 54 } 55 System.out.println(r.name+"...."+r.sex); 56 r.bool=false; 57 r.notify(); 58 } 59 } 60 }
这样问题便得到了解决
可是我不满足于现在的单线程输入 单线程输出;搞点猛地多线程输入 多线程输出。引出经典操作“多生产者多消费者”
多生产者多消费者:
在上面的代码基础上简单的修改成买卖鸭子,并实现多生产者多消费者,看看会出什么问题:
1 public class Text2 { 2 public static void main(String[] args) { 3 Ziyuan r=new Ziyuan(); //创建资源 4 Inp a=new Inp(r); //创建输入任务 5 Outp b=new Outp(r); //创建输出任务 6 Thread t0=new Thread(a); //创建输入线程,执行路径(执行输入任务) 7 Thread t1=new Thread(a); //创建输出线程,执行路径(执行输出任务) 8 Thread t2=new Thread(b); 9 Thread t3=new Thread(b); 10 t0.start(); //开启线程 11 t1.start(); 12 t2.start(); 13 t3.start(); 14 } 15 } 16 class Ziyuan{ 17 boolean bool=false; 18 int a=0; 19 } 20 class Inp implements Runnable{ 21 Ziyuan r; 22 Inp(Ziyuan r){ 23 this.r=r; 24 } 25 public void run(){ 26 while(true){ 27 synchronized(r){ 28 if(r.bool){ 29 try {r.wait();} catch (InterruptedException e) {} 30 } 31 r.a++; 32 System.out.println(Thread.currentThread().getName()+"生产鸭子。。。"+r.a); 33 r.bool=true; 34 r.notify(); 35 } 36 } 37 } 38 } 39 class Outp implements Runnable{ 40 Ziyuan r; 41 Outp(Ziyuan r){ 42 this.r=r; 43 } 44 public void run(){ 45 while(true) 46 synchronized(r){ 47 if(!r.bool){ 48 try {r.wait();} catch (InterruptedException e) {} 49 } 50 System.out.println(Thread.currentThread().getName()+"卖出鸭子"+r.a--); 51 r.bool=false; 52 r.notify(); 53 } 54 } 55 }
28 47 行使用的是if判断,这就表示如果线程0在生产了鸭子后,冻结在28行处的1线程醒了,那么他不会在判断,而是继续生产鸭子,这就导致错误数据的出现(这时将if换成while)。
换成了while后发现,出现了死锁情况(原因:比如.线程1.2.3已经进入了冻结状态,线程0进冻结前,唤醒了t1线程,然后t1获得了CPU的执行权,在t1进行了判断后也进入了冻结状态,这是所有的线程都进入了冻结状态)。
只要我们保证一定能够唤醒对方线程,那么就能解决问题,所以使用notifyAll()方法替换notify()方法就可以解决问题。
注意:1.if只有一次判断,会导致不该运行的线程运行 2.while判断标记,解决了线程获得执行权之后是否要运行的问题,如果while和notify一起使用会导致死锁 3.notifyAll解决了一定会唤醒对方线程的问题(如果己方线程总是获得CPU执行权,那么效率会降低)
Lock接口和Condition接口
如果只是为了唤醒对方线程,而去唤醒所有线程,很明显是不明智的,所以在JDK1.5版本后为我们提供了解决方法
Lock: lock()获取锁 unlock()释放锁 ReentrantLock(已知实现子类) Lock代替了synchronized方法和语句的使用
Condition: await()冻结线程 signal()唤醒一个线程 signalAll()唤醒所有线程 Condition代替了Objcet监视器方法的使用
同步中,一把锁只能有一组监视器,但是Lock锁已有多组Condition监视器
如果线程发生了异常,那么锁将无法释放,所以unlock()一定要放在finally中
使用升级后的JDK所提供的方法重新写代码,提高效率
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.Lock; 3 import java.util.concurrent.locks.ReentrantLock; 4 5 6 public class Text2 { 7 public static void main(String[] args) { 8 Ziyuan r=new Ziyuan(); 9 Inp in=new Inp(r); 10 Outp out=new Outp(r); 11 Thread t0=new Thread(in); 12 Thread t1=new Thread(in); 13 Thread t2=new Thread(out); 14 Thread t3=new Thread(out); 15 t0.start(); 16 t1.start(); 17 t2.start(); 18 t3.start(); 19 20 } 21 } 22 class Ziyuan{ 23 private String name; 24 private String sex; 25 private boolean bool=false; 26 private int mun=1; 27 Lock suo=new ReentrantLock(); 28 Condition inJian=suo.newCondition(); 29 Condition outJian=suo.newCondition(); 30 31 void show(String name){ 32 try{ 33 suo.lock(); 34 while(bool){ 35 try {inJian.await();} catch (InterruptedException e) {} 36 } 37 this.name=name+mun++; 38 System.out.println(Thread.currentThread().getName()+"生产。。。"+this.name); 39 bool=true; 40 outJian.signal(); 41 }finally{ 42 suo.unlock(); 43 } 44 45 } 46 void show1(){ 47 try{ 48 suo.lock(); 49 while(!bool){ 50 try {outJian.await();} catch (InterruptedException e) {} 51 } 52 System.out.println(Thread.currentThread().getName()+"消费.................."+name); 53 bool=false; 54 inJian.signal(); 55 }finally{ 56 suo.unlock(); 57 } 58 59 } 60 } 61 class Inp implements Runnable{ 62 Ziyuan r; 63 Inp(Ziyuan r){ 64 this.r=r; 65 } 66 public void run(){ 67 while(true){ 68 r.show("烤鸭"); 69 } 70 } 71 } 72 class Outp implements Runnable{ 73 Ziyuan r; 74 Outp(Ziyuan r){ 75 this.r=r; 76 } 77 public void run(){ 78 while(true){ 79 r.show1(); 80 } 81 } 82 }
wait和sleep的区别:
1:wait可以指定时间,也可以不指定
sleep一定要指定时间
2:wait释放执行权,释放锁
sleep释放执行权,不释放锁
停止线程:
stop() 过时了,存在安全隐患
run() 方法结束(run方法运行完了,该线程会自动结束)(使用标记结束run方法),代码如下:
1 class Producer implements Runnable 2 { 3 int a=0; 4 boolean bool=true; //标记 5 public void run() 6 { 7 while(bool){ 8 System.out.println(Thread.currentThread().getName()+" "+a++); 9 } 10 } 11 public void set(boolean bool){ //修改标记,用来控制run()方法的结束 12 this.bool=bool; 13 } 14 15 } 16 class ProducerConsumerDemo2 17 { 18 public static void main(String[] args) 19 { 20 Producer pro = new Producer(); 21 Thread t0 = new Thread(pro); 22 t0.start(); 23 for(int i=0;i<500;i++){ 24 System.out.println(Thread.currentThread().getName()+".................."+i); 25 if(i==499){ 26 pro.set(false); 27 } 28 29 } 30 System.out.println(Thread.currentThread().getName()+"..................................."+"over"); 31 } 32 }
如果线程处于冻结状态,那么利用标记来结束线程是行不通的;所以,Thread提供了interrupt()方法强制唤醒线程(因此会抛异常)
1 class Producer implements Runnable 2 { 3 int a=0; 4 boolean bool=true; //标记 5 public synchronized void run() 6 { 7 while(bool){ 8 try { 9 wait(); 10 } 11 catch (InterruptedException e) { 12 bool=false; //因为这中唤醒机制会发生InterruptedException异常,一定会运行catch 所以一般吧标记写在catch中 13 } 14 System.out.println(Thread.currentThread().getName()+" "+a++); 15 } 16 } 17 } 18 class ProducerConsumerDemo2 19 { 20 public static void main(String[] args) 21 { 22 Producer pro = new Producer(); 23 Thread t0 = new Thread(pro); 24 t0.start(); 25 for(int i=0;i<500;i++){ 26 System.out.println(Thread.currentThread().getName()+".................."+i); 27 if(i==499){ 28 t0.interrupt(); 29 } 30 31 } 32 System.out.println(Thread.currentThread().getName()+"..................................."+"over"); 33 } 34 }
守护线程(后台线程):
steDaemon(boolean) (当参数为true的时候这个线程为守护线程)
后台和前台线程基本一样,唯一不同的就是,当前台线程全部运行结束后,后台线程也会跟着结束。(当正在运行的程序全都为守护线程的时候,JAVA虚拟机会自动退出)
其他方法:
join() 一般临时加入一个线程执行运算,会调用这个方法(假设有t0 t1 main三个线程,如果t0调用了这个方法,那么主线程会冻结,t0 t1抢夺CPU的执行权,当t0运算完成,main才接触冻结状态)
setPriority(int) 设置线程的优先级(1-10)一般分为三个等级(1-5-10)注意并不是设置成了10这个最高级,这个线程就会拼命运行,只不过是CPU比较照顾一点。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:数组
- 最详细的java多线程教程来了 2020-06-08
- 系统化学习多线程(一) 2020-06-08
- 多线程:生产者消费者(管程法、信号灯法) 2020-06-01
- 如何合理地估算线程池大小? 2020-05-31
- 那些面试官必问的JAVA多线程和并发面试题及回答 2020-05-28
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash