JAVA并发 线程间的消息传递

概要

线程间的通信是用volatile和synchronized两个关键字实现同步完成的线程间的通信;但是在JAVA中的线程之间的通信其实就是共享内存,当一个变量被volatile修饰或者被同步块包括时,那么线程的操作会实时更新到共享内存,然后各个线程都会知道最新变量的值,也就是内存的可见性;看起来实现了线程间的通信,但是实际是共享内存。关于Volatile的详解到JAVA并发Volatile

特点

  1. 这种方式的本质是共享数据,而不是传递数据;只是从结果上看。数据好像从写线程传递到了读线程。
  2. 这通通信机制无法指定特定的线程接受消息,具体要哪一个接受消息,由操作系统决定。
  3. 总的来说不是真正意义上的通信,是共享数据。

例子

 1 private volatile static boolean runing=false;
 2     public static void main(String[] args) {
 3         Thread t1=new Thread(new Runnable() {
 4             
 5             @Override
 6             public void run() {
 7                 while(!runing) {
 8                     try {
 9                         Thread.sleep(1000);
10                     } catch (InterruptedException e) {
11                         // TODO Auto-generated catch block
12                         e.printStackTrace();
13                     }
14                 }
15                 
16             }
17         });
18         
19         t1.start(); 
20     }
21 public void start() {
22         runing=true;
23     }

等待通知机制

实现方式

  1. wait():将当前线程状态改为等待状态,加入等待队列,释放占用锁;直到当线程发生中断或者调用notify方法,这条线程才会从等待队列转移到同步队列开始竞争锁。
  2. wait(long):和wait一样,只不过多了一个超时动作。一旦超时,就会继续执行wait后面的代码,它不会抛出异常。
  3. notify():将等待队列中的一条线程转移到同步队列中去。
  4. notifyAll():将等待队列中的所有的线程都转移到同步队列中去。

注意

  1. 以上方法必须放在一个同步块中。
  2. 并且以上方法只能够方法所处的同步块的锁对象调用。
  3. 锁对象A.notify只能够唤醒A.wait()。
  4. 调用notify/notifyAll函数仅仅是将线程从等待队列转移到阻塞队列,只有当线程竞争到资源锁时,才能够从wait中返回,继续执行接下来的代码。

例子

 1 Thread t2=new Thread(new Runnable() {
 2             
 3             @Override
 4             public void run() {
 5                 while(!runing) {
 6                     try {
 7                         wait();
 8                         System.out.println("wait after");
 9                     } catch (InterruptedException e) {
10                         // TODO Auto-generated catch block
11                         e.printStackTrace();
12                     }
13                 }
14             }
15         });
16         t2.start();
17         
18         Thread t3=new  Thread(new Runnable() {
19             
20             @Override
21             public void run() {
22                 runing=true;
23                 notifyAll();
24             }
25         });

运行结果

1 Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
2     at java.lang.Object.wait(Native Method)
3     at java.lang.Object.wait(Object.java:502)
4     at javaTest.ThreadApi$1.run(ThreadApi.java:35)
5     at java.lang.Thread.run(Thread.java:748)

具体问题请看注意点

重要问题

①为什么wait必须放在同步块中调试。

因为同步/等待机制需要和共享变量配合使用,一般是先检查状态,再执行。因此会对这个过程加一把锁,确保其原子性运行。

②为什么notify要加锁?还必须和wait同一把锁

首先加锁是为了内存的可见性,使其发生的修改能够及时显示给其他线程;和wait一起使用是保证wait和notify之间的互斥,即:同一时刻,只能有其中一条线程运行。

③为什么必须使用同步块的锁对象调用wait函数和notify函数。

调用wait函数:由于wait要释放锁,所有通过锁对象告诉是哪个要释放锁,然后告诉线程你是在哪个锁上等待的,只有当前锁对象调用notify时才会被唤醒。

管道流

管道流用于两个线程之间的字符流动或者字节流动;

管道流主要:PipedOutputSTream,PipedInputStream,PipedWriter,PipedReader。

他们和io的区别是:Io流是在硬盘,内存,socket之间流动,管道流是在线程之间流动。

实现

 1     static PipedWriter out=new PipedWriter();
 2     static PipedReader in =new PipedReader();
 3     class WriteThread extends Thread{
 4         private PipedWriter out;
 5         
 6         public WriteThread(PipedWriter out) {
 7             this.out=out;
 8         }
 9         public void run() {
10             try {
11                 out.write("hello world");
12             } catch (IOException e) {
13                 // TODO Auto-generated catch block
14                 e.printStackTrace();
15             }
16         }
17     }
18     
19     class ReadThread extends Thread{
20         private PipedReader in;
21         public ReadThread(PipedReader in) {
22             this.in=in;
23         }
24         public void run() {
25             try {
26                 in.read();
27             } catch (IOException e) {
28                 // TODO Auto-generated catch block
29                 e.printStackTrace();
30             }
31         }
32     }
33     
34     public static void main(String[] args) throws IOException {
35         out.connect(in);
36     }

Join

join能够使并发的多线程串行运行

join属于Thread类,通过一个Thread对象调用。当在线程B中执行ThreadA.join时,线程B会被阻塞,等到线程A运行完成。

被等待的那条线程可能会执行很长时间,因此join函数会抛出InterruptedException。当调用threadA.interrupt()后,join函数就会抛出该异常。

实现

 1 public static void main(String[] args){
 2 
 3     // 开启一条线程
 4     Thread t = new Thread(new Runnable(){
 5         public void run(){
 6             // doSometing
 7         }
 8     }).start();
 9 
10     // 调用join,等待t线程执行完毕
11     try{
12         t.join();
13     }catch(InterruptedException e){
14         // 中断处理……
15     }
16 
17 }

 

posted @ 2018-09-21 10:01  轻抚丶两袖风尘  阅读(4375)  评论(0编辑  收藏  举报