首页 > 解决方案 > 如何正确使用通知和等待

问题描述

如何正确使用通知和等待。我只有两个线程来测试我的逻辑。是不是如下我创建了一个线程,它将执行get类型的命令并使用cmdfile_1 file_2创建。我的第一个任务没有,因此它将在条件谓词上置于等待状态,该条件谓词检查两个文件是否都存在于文件上的 .exists() 方法。第二个线程将创建并通知第一个线程,然后另一个线程将唤醒并执行他的工作。问题是,在等待的线程唤醒后,它没有他的工作,而是另一个线程的工作。例如,如果必须执行并且它处于等待状态。并且必须执行所以它创建并通知file3typefile_2file_2Thread-1file1 file2 > file3Thread-2file3 file4 > file2file2Thread-1. 在 Thread-1 醒来后,它拥有file3 fil4 > file2而不是他的文件。我将所有内容都转储到控制台,当两个线程从命令文件中获取任务时,它们会获取正确的任务,但它们没有相同的任务,但在Thread-1醒来后它就有了Thread-2任务。有人可以帮助我了解如何在这种情况下以及在任何其他情况下使用等待和通知。我在实践中阅读了 Java 并发,但他们只在其中放置和获取方法,等待和通知,而且谷歌中的示例很天真,它们按预期工作。提前致谢。

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();
    }

标签: javamultithreading

解决方案


在您的情况下,相互等待通知调用的问题是一个线程可以notify在另一个线程调用之前完成其工作并调用wait。通知不会“记住”,因此如果在实际调用之前调用了通知wait,那么等待线程将永远等待(嗯,直到另一个通知)。

如果我没记错的话,你希望 T1 必须做一些工作,等待来自 T2 的一些通知,然后做一些其他的 + 退出。CountDownLatch如果这是正确的,那么您使用或使用起来会简单得多Semaphore

两者都可以取消上述赛车条件的影响。当其他线程等待它这样做时,或者在这将导致“等待线程”永远不必等待“门已经打开”时,倒计时锁可以“倒计时”。


推荐阅读