Java 如何正确使用条件来等待并通知所有人,而不丢失通知?
我有一组任务,它们被分为两部分,分别是Java 如何正确使用条件来等待并通知所有人,而不丢失通知?,java,multithreading,Java,Multithreading,我有一组任务,它们被分为两部分,分别是enque和deque,以确保合格的(多个)首先运行,然后按顺序运行其他合格的(类似优先级队列) 在enque中,将检查任务,仅执行合格的任务,而阻止其他不合格的任务 在deque中,完成的任务将从队列中删除,然后notifyAll被阻止的任务(实际上是所有其他线程选择合格的) 以下是我试图实现的一个简化演示: class MyTaskQueue { private static final Object THE_QUEUE_LOCK = new O
enque
和deque
,以确保合格的(多个)首先运行,然后按顺序运行其他合格的(类似优先级队列)
在enque
中,将检查任务,仅执行合格的
任务,而阻止其他不合格的
任务
在deque
中,完成的任务将从队列中删除,然后notifyAll
被阻止的任务(实际上是所有其他线程选择合格的)
以下是我试图实现的一个简化演示:
class MyTaskQueue {
private static final Object THE_QUEUE_LOCK = new Object();
public static Map<String, ReentrantLock> taskGroupLock = new HashMap<>();
public static Map<String, Condition> taskGroupCondition = new HashMap<>();
public static void enque(String name, String taskId) {
synchronized (THE_QUEUE_LOCK) {
taskGroupLock.putIfAbsent(name, new ReentrantLock());
taskGroupCondition.putIfAbsent(name, taskGroupLock.get(name).newCondition());
}
synchronized (taskGroupLock.get(name)) {
while (true) {
if (isValid(taskId)) {
break; // Go!!;
} else {
try {
taskGroupCondition.get(name).wait(); // blocked if it's not allowed;
} catch (InterruptedException ignored) {
ignored.printStackTrace();
}
}
}
}
}
public static void deque(String name, String taskId) {
if (taskGroup.containsKey(name) && taskGroup.get(name).contains(taskId)) {
synchronized (THE_QUEUE_LOCK) {
taskGroup.get(name).remove(taskId);
if (taskGroup.get(name).isEmpty()) {
taskGroup.remove(name);
}
synchronized (taskGroupLock.get(name)) {
taskGroupCondition.get(name).notifyAll();
}
}
}
}
}
类MyTaskQueue{
private static final Object_QUEUE_LOCK=new Object();
public static Map taskGroupLock=new HashMap();
public static Map taskGroupCondition=new HashMap();
公共静态void enque(字符串名称、字符串taskId){
已同步(队列锁定){
putIfAbsent(名称,new ReentrantLock());
taskGroupCondition.putIfAbsent(名称,taskGroupLock.get(名称).newCondition());
}
已同步(taskGroupLock.get(名称)){
while(true){
if(isValid(taskId)){
休息;//走!!;
}否则{
试一试{
taskGroupCondition.get(name).wait();//如果不允许,将被阻止;
}捕获(InterruptedException被忽略){
已忽略。printStackTrace();
}
}
}
}
}
公共静态void deque(字符串名称、字符串taskId){
if(taskGroup.containsKey(名称)和&taskGroup.get(名称).contains(任务ID)){
已同步(队列锁定){
taskGroup.get(name).remove(taskId);
if(taskGroup.get(name.isEmpty()){
任务组。删除(名称);
}
已同步(taskGroupLock.get(名称)){
taskGroupCondition.get(name.notifyAll();
}
}
}
}
}
目前,虽然我检查了所有其他任务(至少大多数)是否被正确阻止,但只执行第一个任务
但是当我检查taskGroupCondition.get(name)
时,firstwater
和lastwater
都是null
我错过了什么
任何帮助都将不胜感激 如果我理解正确,您会问以下问题:
并行运行的线程请求从您的MyTaskQueue
权限开始运行(通过enque
方法)
enque
MyTaskQueue
的方法将阻塞,直到请求运行的任务得到确认
deque
方法向MyTaskQueue
声明它已结束运行deque
方法通知所有其他任务,以便检查其中哪些任务是合格的,然后这些任务开始运行import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
public class MyTaskQueue {
private final Map<String, Set<String>> runningTasks;
private String qualifiedTaskId;
public MyTaskQueue(String initialQualifiedTaskId) {
runningTasks = new HashMap<>();
qualifiedTaskId = initialQualifiedTaskId;
}
private synchronized boolean isValid(String taskId) {
return qualifiedTaskId != null && taskId != null && taskId.equals(qualifiedTaskId); //Do your qualification tests here...
}
public synchronized void setQualifiedTaskId(String qualifiedTaskId) {
this.qualifiedTaskId = qualifiedTaskId;
notifyAll(); //Now that the qualification test changed, is time to notify every blocked task.
//This way, all new qualified tasks will also be started. This "notifyAll()" operation is optional.
}
public synchronized void enque(String task, String taskId) {
while (!isValid(taskId)) { //Reentrant lock.
System.out.println("Blocking unqualified task {\"" + task + "\", \"" + taskId + "\"}...");
try { wait(); } catch (InterruptedException ie) { /*Handle the exception...*/ }
}
runningTasks.putIfAbsent(task, new HashSet<>());
runningTasks.get(task).add(taskId);
System.out.println("Starting qualified task {\"" + task + "\", \"" + taskId + "\"}...");
}
//Optional method. Might be needed for example if a Thread
//wants to check if another task is currently running...
public synchronized boolean isRunning(String task, String taskId) {
return runningTasks.containsKey(task) && runningTasks.get(task).contains(taskId);
}
public synchronized void deque(String task, String taskId) {
if (isRunning(task, taskId)) { //Reentrant lock.
//Cleanup:
runningTasks.get(task).remove(taskId);
if (runningTasks.get(task).isEmpty())
runningTasks.remove(task);
//Notify all blocked tasks:
notifyAll();
}
}
public static void main(final String[] args) {
MyTaskQueue q = new MyTaskQueue("qualified");
Random rand = new Random();
new MyThread(q, "Task1", "qualified222", 2500 + rand.nextInt(500)).start();
new MyThread(q, "Task2", "qualified222", 2500 + rand.nextInt(500)).start();
new MyThread(q, "Task3", "qualified", 2500 + rand.nextInt(500)).start();
new MyThread(q, "Task4", "qualified", 2500 + rand.nextInt(500)).start();
new MyThread(q, "Task5", "foreverBlocked", 2500 + rand.nextInt(500)).start();
try { Thread.sleep(3000); } catch (InterruptedException ie) { /*Handle the exception...*/ }
synchronized (q) {
System.out.println("Qualifying tasks of id \"qualified222\"...");
q.setQualifiedTaskId("qualified222"); //Reentrant lock.
}
//Execution of main method never ends, because of the forever blocked task "Task5".
//The "Task5" still runs while waiting for permission... See MyThread for details...
}
}
MyThread
是执行所需实际操作的类
为简单起见,我假设一个任务的id等于一个变量(即qualifiedTaskId
),则该任务是合格的
还有一种main
方法来测试代码
以下是示例输出(我对行进行了编号):
然后,调用第8行到第10行,因为合格任务已结束(因此它们被重新阻止)。
然后,调用第11到13行,因为另一个符合条件的任务已结束(因此它们被重新阻止)。
然后,在第14行到第19行中,鉴定测试更改,新的鉴定任务开始运行。还有另一个任务(
Task5
)尚未通过鉴定。最后,调用第20到21行是因为任务id等于
“qualified222”
结束的合格任务。合格任务以并行或串行顺序运行?@thanopi57感谢您的帮助,是的,它们是并行的。优雅的解决方案。非常感谢你。
public class MyThread extends Thread {
private final String task, taskId;
private final int actionTime; //Dummy uptime to simulate.
private final MyTaskQueue q;
public MyThread(MyTaskQueue q, String task, String taskId, int actionTime) {
this.q = q;
this.task = task;
this.taskId = taskId;
this.actionTime = actionTime;
}
@Override
public void run() {
q.enque(task, taskId); //Wait for permission to run...
System.out.println("Task {\"" + task + "\", \"" + taskId + "\"} is currently running...");
//Now lets actually execute the task of the Thread:
try { Thread.sleep(actionTime); } catch (InterruptedException ie) { /*Handle the exception.*/ }
q.deque(task, taskId); //Declare Thread ended.
}
}