线程之间的通信是什么样的?假设有两个线程A和B,这两个线程在各自执行一个动作之后要让对方知道自己执行了这个动作,又或者这两个线程操作的是同一资源,这就是线程间的通信了。下面,我就用一个例子的多种实现来说明Java如何实现线程间的通信。
例子是这样的,用两个线程,一个输出字母,一个输出数字,交替输出,这样得到的结果就是类似这样的字符串"A1B2C3D4E5...26Z"。
实现方式一:使用一个成员变量作为标记结合自旋锁实现线程通信,其中,成员变量需加上volatile关键字。代码如下:
public class MethodOne {
enum ThreadRunFlag {T1, T2}
private volatile static ThreadRunFlag threadRunFlag = ThreadRunFlag.T1;
public static void main(String[] args) {
String[] letterStringArray = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"
, "K", "L", "M", "N","O", "P", "Q", "R", "S", "T"
, "U", "V", "W", "X", "Y", "Z"};
String[] numStringArray = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
, "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"
, "21", "22", "23", "24", "25", "26"};
method(letterStringArray, numStringArray);
}
private static void method(String[] letterStringArray, String[] numStringArray) {
new Thread(() -> {
for(String str : letterStringArray) {
while(threadRunFlag != ThreadRunFlag.T1) {}
System.out.print(str);
threadRunFlag = ThreadRunFlag.T2;
}
}, "t1").start();
new Thread(() -> {
for(String str : numStringArray) {
while(threadRunFlag != ThreadRunFlag.T2) {}
System.out.print(str);
threadRunFlag = ThreadRunFlag.T1;
}
}, "t2").start();
}
}
实现方式二:使用synchronized与notify、wait方法配合使用实现线程通信。代码如下:
public class MethodTwo {
public static void main(String[] args) {
String[] letterStringArray = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"
, "K", "L", "M", "N","O", "P", "Q", "R", "S", "T"
, "U", "V", "W", "X", "Y", "Z"};
String[] numStringArray = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
, "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"
, "21", "22", "23", "24", "25", "26"};
method(letterStringArray, numStringArray);
}
private static void method(String[] letterStringArray, String[] numStringArray) {
final Object obj = new Object();
new Thread(() -> {
synchronized (obj) {
for(String str : letterStringArray) {
System.out.print(str);
try {
obj.notify();
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 这里使用一个notify是因为在for循环的最后一次后会有一个线程wait
obj.notify();
}
}, "t1").start();
new Thread(() -> {
synchronized (obj) {
for(String str : numStringArray) {
System.out.print(str);
try {
obj.notify();
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 这里使用一个notify是因为在for循环的最后一次后会有一个线程wait
obj.notify();
}
}, "t2").start();
}
}
实现方式三:使用可重入锁实现线程通信。代码如下:
public class MethodThree {
public static void main(String[] args) {
String[] letterStringArray = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"
, "K", "L", "M", "N","O", "P", "Q", "R", "S", "T"
, "U", "V", "W", "X", "Y", "Z"};
String[] numStringArray = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
, "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"
, "21", "22", "23", "24", "25", "26"};
method(letterStringArray, numStringArray);
}
private static void method(String[] letterStringArray, String[] numStringArray) {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
new Thread(() -> {
try {
lock.lock();
for(String str : letterStringArray) {
System.out.print(str);
condition2.signal();
condition1.await();
}
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
try {
lock.lock();
for(String str : numStringArray) {
System.out.print(str);
condition1.signal();
condition2.await();
}
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t2").start();
}
}
实现方式四:使用LockSupport提供的方法实现线程通信。代码如下:
public class MethodFour {
private static Thread t1 = null;
private static Thread t2 = null;
public static void main(String[] args) {
String[] letterStringArray = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"
, "K", "L", "M", "N","O", "P", "Q", "R", "S", "T"
, "U", "V", "W", "X", "Y", "Z"};
String[] numStringArray = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
, "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"
, "21", "22", "23", "24", "25", "26"};
method(letterStringArray, numStringArray);
}
private static void method(String[] letterStringArray, String[] numStringArray) {
t1 = new Thread(() -> {
for(String str : letterStringArray) {
System.out.print(str);
LockSupport.unpark(t2); // 叫醒t2
LockSupport.park(); // 阻塞t1
}
}, "t1");
t2 = new Thread(() -> {
for(String str : numStringArray) {
LockSupport.park(); // 阻塞t2
System.out.print(str);
LockSupport.unpark(t1); // 叫醒t1
}
},"t2");
t1.start();
t2.start();
}
}
以上用四种方式实现了线程的通信,弄明白了这四种写法可以对Java中多线程的处理有一个大致清晰的逻辑。当然,除了这四种外,还有其他的实现方式,但这四种方式相对逻辑清晰简洁,较其他方式常用。