Java 如何正确使用通知和等待
如何正确使用通知和等待。我只有两个线程来测试我的逻辑。如下所示,我创建了一个线程,它将执行getJava 如何正确使用通知和等待,java,multithreading,Java,Multithreading,如何正确使用通知和等待。我只有两个线程来测试我的逻辑。如下所示,我创建了一个线程,它将执行getfile\u 1 file\u 2类型的命令,并使用typecmd创建file3。我的第一个任务没有文件\u 2,因此它将在一个条件谓词上处于等待状态,该条件谓词将检查这两个文件是否在文件上都存在.exists()方法。第二个线程将创建文件2,它将通知第一个线程,然后另一个线程将唤醒并执行其作业。问题是,在等待的线程醒来后,它没有他的工作,而是另一个线程的工作。例如,如果Thread-1必须执行fil
file\u 1 file\u 2
类型的命令,并使用type
cmd创建file3
。我的第一个任务没有文件\u 2
,因此它将在一个条件谓词上处于等待状态,该条件谓词将检查这两个文件是否在文件上都存在.exists()方法。第二个线程将创建文件2
,它将通知第一个线程,然后另一个线程将唤醒并执行其作业。问题是,在等待的线程醒来后,它没有他的工作,而是另一个线程的工作。例如,如果Thread-1
必须执行file1 file2>file3
并且处于等待状态。而Thread-2
必须执行file3 file4>file2
,因此它会创建file2
并通知Thread-1
。线程1醒来后,它拥有file3 fil4>file2
而不是他的文件。我正在将所有内容转储到控制台,当两个线程从命令文件中执行任务时,它们执行正确的任务,它们没有相同的任务,但在Thread-1
唤醒后,它执行Thread-2
任务。有人能帮我理解如何在这种情况下以及在任何其他情况下使用wait和notify吗。我在实践中读过Java并发性,但他们只在那里使用wait和notify方法,而且google中的示例很幼稚,它们可以按预期工作。提前谢谢
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class MyTaskManager {
private static AtomicInteger id = new AtomicInteger();
private static String[] in;
private static String cmd;
private static Task task;
private static Process process;
private static MyTaskManager taskManager;
public static void main(String[] args) {
taskManager = new MyTaskManager();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
// Assign work
synchronized (taskManager) {
String line = Files.readAllLines(Paths.get("commands.txt"))
.get(id.get());
id.getAndIncrement();
in = line.split(" ");
cmd = in[0] + " " + in[1] + " " + in[2] + " " + in[3] + " " + in[4];
task = new Task(cmd);
System.out.println(cmd);
}
// After the thread is woked up it checks the condition again
// but this time it is taken the other thread object
synchronized (taskManager) {
while (!task.checkCondition(task)) {
System.out.println("Waiting thread " + Thread.currentThread().getName());
System.out.println("Write file in wait " + task.getOutput_file());
System.out.println("---------------------------------");
taskManager.wait();
System.out.println(Thread.currentThread().getName() + " after sleep");
}
}
process = Runtime.getRuntime()
.exec("cmd /c start cmd.exe /k \"" + task.getCmd() + "\"");
process.waitFor();
synchronized (taskManager) {
taskManager.notifyAll();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
// Assign work
synchronized (taskManager) {
String line = Files.readAllLines(Paths.get("commands.txt"))
.get(id.get());
id.getAndIncrement();
in = line.split(" ");
cmd = in[0] + " " + in[1] + " " + in[2] + " " + in[3] + " " + in[4];
task = new Task(cmd);
System.out.println(cmd);
}
process = Runtime.getRuntime()
.exec("cmd /c start cmd.exe /k \"" + task.getCmd() + "\"");
process.waitFor();
synchronized (taskManager) {
taskManager.notifyAll();
System.out.println("Notifying " + Thread.currentThread().getName());
}
} catch (IOException | InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
});
BlockingQueue<Runnable> worksQueue = new
ArrayBlockingQueue<>(10);
RejectedExecutionHandler rejectionHandler = new
RejectedExecutionHandlerImpl();
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 20,
TimeUnit.SECONDS, worksQueue, rejectionHandler);
executor.prestartAllCoreThreads();
List<Runnable> taskGroup = new ArrayList<>();
taskGroup.add(t);
taskGroup.add(t1);
worksQueue.add(new MultiRunnable(taskGroup));
executor.shutdown();
}
}
在您的例子中,相互等待通知调用的问题是,一个线程可以在另一个线程调用
wait
之前完成其任务并调用notify
。通知不会被“记住”,所以若在实际的wait
之前调用notify,那个么等待线程将永远等待(直到另一个通知)
如果我没有弄错的话,您希望T1必须做一些工作,等待T2的一些通知,然后再做一些其他+退出。如果这是正确的,那么使用倒计时闩锁
或信号量
两者都可以抵消上述比赛条件的影响。倒计时闩锁可以在其他线程等待时“倒计时”,也可以在“等待线程”之前“倒计时”,这将导致“等待线程”永远不必等待,因为“门已打开”。是的,这正是我想要的。但是你知道为什么这不能像我希望的那样使用wait和notify吗?是的,很可能在调用
wait
之前调用了notify
。我在编辑中描述过。建议读:“。”@Solomon Slow谢谢
boolean checkCondition(Task task) {
String[] in = task.cmd.split(" ");
File dep1 = new File(in[1]);
File dep2 = new File(in[2]);
return dep1.exists() && dep2.exists();
}