java中实现线程通信的四种方式
1.synchronized同步
多个线程之间可以借助synchronized关键字来进行间接通信,本质上是通过共享对象进行通信。如下:
1 public class SynDemo { 2 3 public synchronized void print1(){ 4 System.out.println(Thread.currentThread().getName()+"执行......"); 5 } 6 7 public synchronized void print2(){ 8 System.out.println(Thread.currentThread().getName()+"执行......"); 9 } 10 11 public static void main(String[] args) { 12 SynDemo synDemo = new SynDemo();//共享对象 13 new Thread(new Mythread1(synDemo)).start(); 14 new Thread(new Mythread0(synDemo)).start(); 15 } 16 17 } 18 19 class Mythread1 implements Runnable{ 20 private SynDemo syn; 21 Mythread1(SynDemo syn){ 22 this.syn =syn; 23 } 24 @Override 25 public void run() { 26 syn.print1(); 27 } 28 29 } 30 31 class Mythread0 implements Runnable{ 32 private SynDemo syn; 33 34 Mythread0(SynDemo syn){ 35 this.syn =syn; 36 } 37 @Override 38 public void run() { 39 syn.print2(); 40 } 41 42 }
结果输出:
Thread-0执行......
Thread-1执行......
2.while轮询的方式
一个线程修改共享对象的某个属性,另外一个线程不断的轮训查看共享对象的这个属性是否发生符合条件的变化。本质上同样是借助共享对象进行通信,通过不断轮训,判断共享对象的某个属性(CPU的cache被刷新后,在另外线程可以看其他线程中对象的属性发生变化)符合条件后打破循环。示例如下:
1 public class WhileDemo { 2 3 private List list = new ArrayList(); 4 5 public void addList(){ 6 System.out.println("list+1"); 7 list.add(1); 8 } 9 10 public int listSize(){ 11 return list.size(); 12 } 13 14 public static void main(String[] args) { 15 WhileDemo WhileDemo = new WhileDemo();//共享对象 16 17 new Thread(new Mythread3(WhileDemo)).start(); 18 new Thread(new Mythread4(WhileDemo)).start(); 19 } 20 21 } 22 23 class Mythread3 implements Runnable{ 24 25 private WhileDemo wd; 26 27 Mythread3(WhileDemo wd){ 28 this.wd =wd; 29 } 30 @Override 31 public void run() { 32 try { 33 Thread.sleep(300); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 wd.addList(); 38 wd.addList(); 39 } 40 41 } 42 43 class Mythread4 implements Runnable{ 44 private WhileDemo wd; 45 46 Mythread4(WhileDemo wd){ 47 this.wd =wd; 48 } 49 @Override 50 public void run() { 51 try { 52 while(true){ 53 if(wd.listSize()==2){ 54 System.out.println(Thread.currentThread().getName()+"达到要求,退出"); 55 break; 56 } 57 //未达到要求 58 System.out.println("暂时不满足要求,继续运行"); 59 Thread.sleep(100); 60 } 61 } catch (Exception e) { 62 e.printStackTrace(); 63 } 64 } 65 66 }
结果输出:
暂时不满足要求,继续运行 暂时不满足要求,继续运行 暂时不满足要求,继续运行 Thread-0 list+1 Thread-0 list+1 Thread-1达到要求,退出
3.waite/notify通信
在Java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信。在线程中调用wait()方法后,将阻塞等待其他线程的通知(其他线程调用notify()方法或notifyAll()方法),当其他线程中调用notify()方法或notifyAll()方法后,被阻塞等待的线程将被唤醒。示例如下:
1 public class WNDemo { 2 3 private List list = new ArrayList(); 4 5 public void addList(){ 6 System.out.println(Thread.currentThread().getName()+" list+1"); 7 list.add(1); 8 } 9 10 public int listSize(){ 11 return list.size(); 12 } 13 14 public static void main(String[] args) { 15 WNDemo WNDemo = new WNDemo();//共享对象 16 new Thread(new Mythread5(WNDemo)).start(); 17 new Thread(new Mythread6(WNDemo)).start(); 18 } 19 20 } 21 22 class Mythread5 implements Runnable{ 23 24 private WNDemo wd; 25 26 Mythread5(WNDemo wd){ 27 this.wd =wd; 28 } 29 @Override 30 public void run() { 31 try { 32 synchronized (wd) { 33 while (wd.listSize()!=5) { 34 System.out.println(Thread.currentThread().getName()+" list大小不满足要求,进入wait状态,等待唤醒"); 35 wd.wait(); 36 } 37 System.out.println(Thread.currentThread().getName()+" list大小满足要求,执行结束"); 38 } 39 } catch (Exception e) { 40 e.printStackTrace(); 41 } 42 43 } 44 45 } 46 47 class Mythread6 implements Runnable{ 48 private WNDemo wd; 49 50 Mythread6(WNDemo wd){ 51 this.wd =wd; 52 } 53 @Override 54 public void run() { 55 try { 56 synchronized (wd) { 57 for(int i=0;i<5;i++){ 58 wd.addList(); 59 } 60 wd.notify();//唤醒处于等待状态wd的线程 61 } 62 } catch (Exception e) { 63 e.printStackTrace(); 64 } 65 } 66 67 }
结果输出:
Thread-0 list大小不满足要求,进入wait状态,等待唤醒 Thread-1 list+1 Thread-1 list+1 Thread-1 list+1 Thread-1 list+1 Thread-1 list+1 Thread-0 list大小满足要求,执行结束
注意:
- wait()与 notify()/notifyAll()方法必须在同步代码块中使用。
- 当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
- 当执行notify/notifyAll方法时,不会立即释放锁。会唤醒一个处于等待该对象锁的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁。
- notifyAll使所有原来在该对象上wait的线程统统退出wait的状态,变成等待获取该对象锁的状态,一旦当等待的对象锁被释放,这些被唤醒的线程进行竞争,获取锁的线程继续执行,其他的继续等待锁的释放。
- wait(long),如果在指定时间了未被唤醒,则自动进入竞争锁状态。也可能是在指定时间内被其他线程唤醒。
4.管道通信
管道流主要用来实现两个线程间二进制数据的流通。示例如下:
1 public class PipeDemo { 2 public static void main(String[] args) { 3 PipedInputStream pis = new PipedInputStream(); 4 PipedOutputStream pos = new PipedOutputStream(); 5 6 try { 7 pis.connect(pos);//连接管道输入流和输出流 8 } catch (IOException e) { 9 e.printStackTrace(); 10 } 11 12 new Thread(new MyThread1(pis)).start(); 13 new Thread(new Mythread2(pos)).start(); 14 15 16 } 17 18 } 19 20 class MyThread1 implements Runnable{ 21 private PipedInputStream pis; 22 23 public MyThread1(PipedInputStream pis){ 24 this.pis = pis; 25 } 26 @Override 27 public void run() { 28 while(true){//不断轮训管道输入流中是否有字节数据 29 try { 30 int count = pis.available(); 31 if(count>0){ 32 System.out.println("开始从管道流中读取数据"); 33 System.out.println(pis.read());//读取一个字节 34 System.out.println("从管道流中读取数据完毕"); 35 break; 36 } 37 System.out.println("管道流中暂时还没有数据"); 38 //如果输入流中暂时没有数据 39 Thread.sleep(1000); 40 } catch (Exception e) { 41 e.printStackTrace(); 42 } 43 } 44 } 45 } 46 47 class Mythread2 implements Runnable{ 48 private PipedOutputStream pos; 49 50 public Mythread2(PipedOutputStream pos){ 51 this.pos = pos; 52 } 53 54 @Override 55 public void run() { 56 try { 57 Thread.sleep(500); 58 System.out.println("开始往管道流写入数据"); 59 pos.write(96);//写入一个字节进管道输出流 60 System.out.println("管道流中写入数据完毕"); 61 } catch (Exception e) { 62 e.printStackTrace(); 63 } 64 } 65 }
结果输出:
管道流中暂时还没有数据 开始往管道流写入数据 管道流中写入数据完毕 开始从管道流中读取数据 96 从管道流中读取数据完毕