在多线程程序中需要各个线程协同完成工作,就需要线程之间进行通信。
为了更好地理解线程间的通信,我们可以模拟这样的一种应用场景,假设有两个线程同时去操作同一个存储空间,其中一个线程负责向存储空间中存入数据,另一个线程负责取出数据。通过一个案例来实现上述情况,首先定义一个类,在类中使用一个数组来表示存储空间,并提供数据的存取方法。
Storage :
public class Storage {
//数据存储数组
private int[] cells = new int[10];
//inPort 表示存入时数组下标,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("从 cells["+outPos+"]中取出数据"+data);
outPos++; //取完元素让位置加1
if (outPos == cells.length)
outPos = 0;
}
}
以上定义的数组 cells 用来存储数据, put ()方法用于向数组存人数据, get()方法用于获取数据。针对数组元素的存取操作都是从第一个元素开始依次进行的,每当操作完数组的最后一个元素时,索引都会被置为0,也就是重新从数组的第一个位置开始存取操作。
Input & Output :
class Input implements Runnable { //输入线程类
private Storage st;
private int num; //定义一个变量 num
Input(Storage st) { //通过构造方法接收一个 Storage 对象
this.st = st;
}
public void run() {
while (true) {
st.put(num++); //将 num存入数组,每次存入后 num自增
}
}
}
class Output implements Runnable { //输出线程类
private Storage st;
Output(Storage st) { //通过构造方法接收一个 Storage 对象
this.st = st;
}
@Override
public void run() {
while (true) {
st.get(); //循环取出元素
}
}
}
以上定义了两个类 Input 和 Output ,它们都实现了的 Runnable 接口,并且构造方法中都接收一个 Storage 类型的对象。在 Input 类的 run ()方法中使用 while 循环不停地向存储空间中存入数据 num ,并在每次存人数据后将 num 进行自增,从而实现存人自然数1、2、3、4…的效果。在 Output 类的 run ()方法中使用 while 循环不停地从存储空间中取出数据。
开启两个线程分别运行 Input 和 Output 类中的代码
ExampleM17
public class ExampleM17 {
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(); //开启新线程
}
}
运行结果中在取出数字12后,紧接着取出的是23,这样的现象明显是不对的。我们希望出现的运行结果是依次取出递增的自然数。之所以出现这种现象是因为在 Input 线程存入数字13时, Output 线程并没有及时取出数据, Input 线程一直在持续地存入数据,直到将数组放满,又从数组的第一位置开始存入21、22、23…,当 Output 线程再次取数据时,取出的不再是13而是23。