多条线程之间有时需要数据交互,下面介绍5种线程间数据交互的方式,他们的使用处景各有不同。
PS:关于volatile的详细介绍请移步至:Java并发编程的艺术(3)——volatile
这两种方式都采取了同步机制实现多条线程间的数据通讯。与其说是“通讯”,倒不如说是“同享变量”来的恰当。当1个同享变量被volatile修饰 或 被同步块包裹后,他们的读写操作都会直接操作同享内存,从而各个线程都能看到同享变量最新的值,也就是实现了内存的可见性。
这类方式能“传递”变量。当需要传递1些公用的变量时就能够使用这类方式。如:传递boolean flag,用于表示状态、传递1个存储所有任务的队列等。
用这类方式实现线程的开关控制。
// 用于控制线程当前的履行状态 private volatile boolean running = false; // 开启1条线程 Thread thread = new Thread(new Runnable(){ void run(){ // 开关 while(!running){
Thread.sleep(1000);
} // 履行线程任务 doSometing();
}
}).start(); // 开始履行 public void start(){
running = true;
}
等待/通知机制的实现由Java完成,我们只需调用Object类的几个方法便可。
// 同享的状态变量 boolean flag = false; // 线程1 Thread t1 = new Thread(new Runnable(){ public void run(){ while(!flag){
wait();
}
}
}).start(); // 线程2 Thread t2 = new Thread(new Runnable(){ public void run(){
flag = true;
notifyAll();
}
}).start();
上述例子thread1未加同步。当thread1履行到while那行后,判断其状态为true,此时若产生上下文切换,线程2开始履行,并1口气履行完了;此时flag已是true,但是thread1继续履行,遇到wait后便进入等待态;但此时已没有线程能唤醒它了,因此就1直等待下去。
为何notify需要加锁?且必须和wait使用同1把锁?
首先,加锁是为了保证同享变量的内存可见性,让它产生修改后能直接写入同享内存,好让wait所处的线程立即看见。
其次,和wait使用同1把锁是为了确保wait、notify之间的互斥,即:同1时刻,只能有其中1条线程履行。
为何必须使用同步块的锁对象调用wait函数?
首先,由于wait会释放锁,因此通过锁对象调用wait就是告知wait释放哪一个锁。
其次,告知线程,你是在哪一个锁对象上等待的,只有当该锁对象调用notify时你才能被唤醒。
为何必须使用同步块的锁对象调用notify函数?
告知notify,只唤醒在该锁对象上等待的线程。
等待/通知机制用于实现生产者和消费者模式。
synchronized(锁A){
flag = true;// 或:list.add(xx); 锁A.notify();
}
synchronized(锁A){ // 不满足条件 while(!flag){ // 或:list.isEmpty() 锁A.wait();
} // doSometing…… }
在之前的生产者-消费者模式中,如果生产者没有发出通知,那末消费者将永久等待下去。为了不这类情况,我们可以给消费者增加超时等待功能。该功能依托于wait(long)方法,只需在wait前的检查条件中增加超时标识位,实现以下:
public void get(long mills){
synchronized( list ){ // 不加超时功能 if ( mills <= 0 ) { while( list.isEmpty() ){ list.wait();
}
} // 添加超时功能 else {
boolean isTimeout = false; while(list.isEmpty() && isTimeout){ list.wait(mills);
isTimeout = true;
} // doSometing…… }
}
}
管道流用于在两个线程之间进行字节流或字符流的传递。
步骤以下:
1. 在1条线程中分别创建输入流和输出流;
2. 将输入流和输出留连接起来;
3. 将输入流和输出流分外传递给两条线程;
4. 调用read和write方法就能够实现线程间通讯。
// 创建输入流与输出流对象 PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader(); // 连接输入输出流 out.connect(in); // 创建写线程 class WriteThread extends Thread{ private PipedWriter out; public WriteThread(PipedWriter out){ this.out = out;
} public void run(){
out.write("hello concurrent world!");
}
} // 创建读线程 class ReaderThread extends Thread{ private PipedReader in; public ReaderThread(PipedReader in){ this.in = in;
} public void run(){
in.read();
}
} //
public static void main(String[] args){ // 开启1条线程 Thread t = new Thread(new Runnable(){ public void run(){ // doSometing }
}).start(); // 调用join,等待t线程履行终了 try{
t.join();
}catch(InterruptedException e){ // 中断处理…… }
}
上一篇 (1)版本控制工具之Git
下一篇 Linux sed编辑器