[英]How to properly use notify and wait
如何正確使用通知和等待。 我只有兩個線程來測試我的邏輯。 是不是如下我創建一個線程,它將執行類型為 get file_1 file_2
的命令並創建type
cmd 的file3
。 我的第一個任務沒有file_2
因此它將在條件謂詞上處於等待狀態,該條件謂詞檢查兩個文件是否都存在文件上的 .exists() 方法。 第二個線程將創建file_2
並通知第一個線程然后另一個線程將喚醒並執行他的工作。 問題是在等待的線程喚醒后,它沒有他的工作,而是另一個線程的工作。 例如,如果Thread-1
必須執行file1 file2 > file3
並且它處於等待狀態。 並且Thread-2
必須執行file3 file4 > file2
所以它創建file2
並通知Thread-1
。 線程 1 醒來后,它有file3 fil4 > file2
而不是他的文件。 我正在將所有內容轉儲到控制台,當兩個線程從命令文件中獲取任務時,它們會獲取正確的任務,但它們沒有相同的任務,但是在Thread-1
喚醒后,它具有Thread-2
任務。 有人可以幫助我了解如何在這種情況下以及在任何其他情況下使用等待和通知。 我已經在實踐中閱讀了 Java Concurrency,但他們只在那里放置和使用方法並等待並通知 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();
}
}
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();
}
在您的情況下,相互 wait-notify 調用的問題在於,一個線程可以完成其工作並在另一個線程調用wait
之前調用notify
。 通知不會“記住”,因此如果在實際wait
之前調用通知,則等待線程將永遠等待(好吧,直到另一個通知)。
如果我沒記錯的話,你想讓 T1 做一些工作,等待 T2 的一些通知,然后做一些其他的 + 退出。 如果這是正確的,那么使用CountDownLatch
或Semaphore
會簡單得多
兩者都可以取消上述賽車條件的影響。 當其他線程正在等待它這樣做時,倒計時閂鎖可以“倒計時”,或者在這之前會導致“等待線程”永遠不必等待,因為“門已經打開”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.