多线程通信:
线程之间进行通信,协同完成工作。例如一条流水线的上下两个工序,他们必须以规定的速率完成各自的工作,才能保证产品再流水线中顺利的流转。如果下工序过慢,会造成产品再两道工序之间的积压,如果上工序过慢,会造成下工序长时间无事可做。线程之间的通信,可以让整个工作更好的完成。
1、下面一个例子,两个线程去操作同一个存储空间,其中一个线程向存储空间存入数据,另一个线程负责取出数据。用此例子来演示线程之间没有进行通信时的结果。
class Storage {
// 数据存储数组
private int[] cells = new int[10];
// inPos表示存入时数组下标,outPos表示取出时数组下标
private int inPos, outPos;
// 定义一个put()方法向数组中存入数据
public void put(int num) {
cells[inPos] = num;
System.out.println("在cells[" + inPos + "]中放入数据---" + cells[inPos]);
inPos++;// 存完元素让位置加1
if (inPos == cells.length)
inPos = 0; // 当inPos为数组长度时,将其置为0
}
// 定义一个get()方法从数组中取出数据
public void get() {
int data = cells[outPos];
System.out.println("从celss[" + outPos + "]中取出数据" + data);
outPos++; // 取完元素让位置加1
if (outPos == cells.length)
outPos = 0;
}
}
public class Input implements Runnable{ // 输入线程类
private Storage st ;
private int num=0; // 定义一个变量num
Input(Storage st){ // 通过构造方法接收一个Storage对象
this.st = st;
}
public void run(){
while(true){
if(num==10){
break;
}
st.put(num++); // 将num存入数组,每次存入后num自增
}
}
}
class Output implements Runnable{ //输出线程类
private Storage st ;
Output(Storage st){
this.st = st;
}
public void run(){
while(true){
st.get();
}
}
}
public class Example17 {
public static void main(String[] args) {
Storage st = new Storage(); // 创建数据存储类对象
Input input = new Input(st); // 创建Input对象传入Storage对象
Output output = new Output(st); // 创建Output对象传入Storage对象
new Thread(input).start(); // 开启新线程
new Thread(output).start(); // 开启新线程
}
}
结果:
在cells[0]中放入数据---0
从celss[0]中取出数据0
在cells[1]中放入数据---1
从celss[1]中取出数据1
在cells[2]中放入数据---2
从celss[2]中取出数据2
在cells[3]中放入数据---3
从celss[3]中取出数据3
从celss[4]中取出数据4
在cells[4]中放入数据---4
从celss[0]中取出数据0
在cells[0]中放入数据---5
从celss[1]中取出数据1
在cells[1]中放入数据---6
从celss[2]中取出数据2
在cells[2]中放入数据---7
从celss[3]中取出数据3
在cells[3]中放入数据---8
从celss[4]中取出数据4
在cells[4]中放入数据---9
2、若想让多个线程之间轮流执行,例如让输入线程输入后,输入线程输出输入的数之后再进行输出,就可以设置多线程通信,来达到目的,如下改进代码所示
详解:再Java中,再Object类中,提供了wait(),notify(),notifyAll()方法用于解决线程间的通信问题。
void wait() :使当前线程放弃同步锁进入等待,直到其他线程进入此同步锁,并调用notify()方法,或notifyAll()方法唤醒该线程为止。
void notify():唤醒此同步锁上等待的第一个调用wait()方法的线程。
void notify():唤醒此同步锁上调用wait()方法的所有线程。
下列改进代码中,首先用synchronized关键字将put()方法和get()方法修饰为同步方法,每个时间点仅有一种操作访问数组,当存入数据时,数组填满,调用同步锁wait()方法使存入数据的线程进入等待状况,让取出数据的线程开始操作,取完之后调用同步锁wait()方法进入等待,之后重复进行。
class Storage {
private int[] cells = new int[5]; // 数据存储数组
private int inPos, outPos; // inPos存入时数组下标,outPos取出时数组下标
private int count; // 存入或者取出数据的数量
public synchronized void put(int num) {
try {
// 如果放入数据等于cells的长度,此线程等待
while (count == cells.length) {
this.wait();
}
cells[inPos] = num; // 向数组中放入数据
System.out.println("在cells[" + inPos + "]中放入数据---" + cells[inPos]);
inPos++;// 存完元素让位置加1
if (inPos == cells.length) // 当在cells[9]放完数据后再从cells[0]开始
inPos = 0;
count++; // 每放一个数据count加1
this.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void get() {
try {
while (count == 0) { // 如果 count为0,此线程等待
this.wait();
}
int data = cells[outPos]; // 从数组中取出数据
System.out.println("从cells[" + outPos + "]中取出数据" + data);
cells[outPos] = 0; // 取出后,当前位置的数据置0
outPos++; // 取完元素让位置加1
if (outPos == cells.length) // 当从cells[9]取完数据后再从cells[0]开始
outPos = 0;
count--; // 每取出一个元素count减1
this.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Input implements Runnable{ // 输入线程类
private Storage st ;
private int num; // 定义一个变量num
Input(Storage st){ // 通过构造方法接收一个Storage对象
this.st = st;
}
public void run(){
while(true){
if(num==10){
break;
}
st.put(num++); // 将num存入数组,每次存入后num自增
}
}
}
class Output implements Runnable{ //输出线程类
private Storage st ;
Output(Storage st){
this.st = st;
}
public void run(){
for(int i=0;i<10;i++){
st.get();
}
}
}
public class Example2 {
public static void main(String[] args) {
Storage st = new Storage(); // 创建数据存储类对象
Input input = new Input(st); // 创建Input对象传入Storage对象
Output output = new Output(st); // 创建Output对象传入Storage对象
new Thread(input).start(); // 开启新线程
new Thread(output).start(); // 开启新线程
}
}
结果:
在cells[0]中放入数据---0
在cells[1]中放入数据---1
在cells[2]中放入数据---2
在cells[3]中放入数据---3
在cells[4]中放入数据---4
从cells[0]中取出数据0
从cells[1]中取出数据1
从cells[2]中取出数据2
从cells[3]中取出数据3
从cells[4]中取出数据4
在cells[0]中放入数据---5
在cells[1]中放入数据---6
在cells[2]中放入数据---7
在cells[3]中放入数据---8
在cells[4]中放入数据---9
从cells[0]中取出数据5
从cells[1]中取出数据6
从cells[2]中取出数据7
从cells[3]中取出数据8
从cells[4]中取出数据9