关于Java同步锁中await和signalAll的使用经验

当使用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

    原文作者:java锁
    原文地址: https://blog.csdn.net/zqzxken/article/details/46364721
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞