JAVA并发线程间的消息传递
概要
线程间的通信是⽤volatile和synchronized两个关键字实现同步完成的线程间的通信;但是在JAVA中的线程之间的通信其实就是共享内存,当⼀个变量被volatile修饰或者被同步块包括时,那么线程的操作会实时更新到共享内存,然后各个线程都会知道最新变量的值,也就是内存的可见性;看起来实现了线程间的通信,但是实际是共享内存。关于Volatile的详解到。
特点
1. 这种⽅式的本质是共享数据,⽽不是传递数据;只是从结果上看。数据好像从写线程传递到了读线程。
2. 这通通信机制⽆法指定特定的线程接受消息,具体要哪⼀个接受消息,由操作系统决定。
3. 总的来说不是真正意义上的通信,是共享数据。
例⼦
1private volatile static boolean runing=false;
2public static void main(String[] args) {
3        Thread t1=new Thread(new Runnable() {
4
5            @Override
6public void run() {
7while(!runing) {
8try {
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    }
21public 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
4public void run() {
5while(!runing) {
6try {
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
21public 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之间流动,管道流是在线程之间流动。
实现
1static PipedWriter out=new PipedWriter();
2static PipedReader in =new PipedReader();
3class WriteThread extends Thread{
4private PipedWriter out;
5
6public WriteThread(PipedWriter out) {
7this.out=out;
8        }
9public void run() {
10try {
11                out.write("hello world");
12            } catch (IOException e) {
13// TODO Auto-generated catch block
14                e.printStackTrace();
15            }
16        }
17    }
18
19class ReadThread extends Thread{
20private PipedReader in;
21public ReadThread(PipedReader in) {
22this.in=in;
23        }
24public void run() {
25try {
26                in.read();
27            } catch (IOException e) {
28// TODO Auto-generated catch block
29                e.printStackTrace();
30            }
31        }
32    }
33
34public static void main(String[] args) throws IOException {
35        t(in);
36    }
Join
join能够使并发的多线程串⾏运⾏
join属于Thread类,通过⼀个Thread对象调⽤。当在线程B中执⾏ThreadA.join时,线程B会被阻塞,等到线程A运⾏完成。
被等待的那条线程可能会执⾏很长时间,因此join函数会抛出InterruptedException。当调⽤threadA.interrupt()后,join函数就会抛出该异常。实现
1public static void main(String[] args){
2
3// 开启⼀条线程
4    Thread t = new Thread(new Runnable(){
5public void run(){
6// doSometing
7        }
8    }).start();pipedinputstream
9
10// 调⽤join,等待t线程执⾏完毕
11try{
12        t.join();
13    }catch(InterruptedException e){
14// 中断处理……
15    }
16
17 }

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。