当使用Lock来保证线程同步时,需使用Condition对象来使线程保持协调。Condition实例被绑定在一个Lock的对象上,使用Lock对象的方法newCondition()获取Condition的实例。Condition提供了下面三种方法,来协调不同线程的同步:
1、await():导致当前线程等待,直到其他线程调用该Condition的signal()或signalAll()方法唤醒该线程。
2、signal():唤醒在此Lock对象上等待的单个线程。
3、signalAll():唤醒在此Lock对象上等待的所有线程。
我在进行《疯狂Java讲义(精粹)第二版》多线程一章的一道习题的编程时遇到了使用了await()和signalAll()进行线程同步,却导致了死锁的问题。在网络搜寻了下,原因是我对await()方法理解有误导致。题目如下:写2个线程,其中一个线程打印1-52,另一个线程打印A-Z,打印顺序应该是12A34B56C……5152Z,需使用多线程通信的知识解决。
附上最终正确运行的代码,同时加上了开始时犯的错误的注释。
1、PrintTask
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PrintTask<T> implements Runnable {
private List<T> charList = new ArrayList<>(); //需打印的队列。由于打印项目既包含字符也包含递增的数字,此处使用泛型
private int period; //每次打印的个数
private int priority; //优先级,即在多个队列中的打印次序
private int total; //打印队列总数
public static int sequence = 0; //当前打印的队列序号
private static final Lock lock = new ReentrantLock(); //由于需要锁定的sequence是类成员,创建一个static锁,保证该类不同线程实例能够感知到signalAll()
private static final Condition condition = lock.newCondition(); //类Condition成员
//private final Lock lock = new ReentrantLock();----------->当不使用static定义Lock和Condition时,由于不同线程为不同的实例,相互之间
//private final Condition condition = lock.newCondition(); 无法感知其他类发出的signalAll(),导致线程之间相互等待却无法得到响应
public PrintTask(List<T> charList, int period, int priority, int total) {
this.charList = charList;
this.period = period;
this.priority = priority;
this.total = total;
}
public void run() {
Iterator<T> iter = charList.iterator();
while(true) {
lock.lock(); //锁定
try {
if (sequence % total == priority) {
for (int i = 0; i < period; i++) {
if(iter.hasNext()) {
System.out.print(iter.next());
}
else {
break;
}
}
++sequence;
condition.signalAll(); //该类其他实例均能接受到该signalAll()方法
}
else {
condition.await(); //类成员condition的await()方法,告知类的所有实例均感知signalAll()
}
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
lock.unlock();
}
}
}
}
2、主程序PrintAZ.java
import java.util.ArrayList;
import java.util.List;
public class PrintAZ {
final static int TOTAL = 2;
public static void main(String[] args) {
//生成数字列表1-52
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 52; i++) {
numbers.add(i);
}
//生成字母列表A-Z
List<Character> chars = new ArrayList<>();
for (char i = 'A'; i <= 'Z'; i++)
{
chars.add(i);
}
//PrintTask(打印队列, 每次打印长度, 次序, 队列总数)
PrintTask<Integer> task1 = new PrintTask<>(numbers, 2, 0, TOTAL);
PrintTask<Character> task2 = new PrintTask<>(chars, 1, 1, TOTAL);
new Thread(task1).start();
new Thread(task2).start();
}
}
当定义Lock和Condition为实例成员变量时,运行结果如下:
12A
Lock和Condition为类成员时,运行结果如下:
12A34B56C78D910E1112F1314G1516H1718I1920J2122K2324L2526M2728N2930O3132P3334Q3536R3738S3940T4142U4344V4546W4748X4950Y5152Z