简体   繁体   中英

How to properly use notify and wait

How to use notify and wait properly. I have just two threads to test my logic. Is it as follows i create one thread which will execute a command of the type get file_1 file_2 and create file3 with the type cmd. My first task don't have file_2 so it will be put in wait state on a condition predicate that checks if the both files are present with .exists() method on the file. The second thread will create file_2 and it will notify the first thread then the other thread will wake up and execute his job. The problem is that after the thread that is waiting wakes up it doesn't have his job but the other's thread job. For example if Thread-1 have to execute file1 file2 > file3 and it is in a wait state. And Thread-2 have to execute file3 file4 > file2 so it creates the file2 and notify Thread-1 . After Thread-1 woke up it have file3 fil4 > file2 and not his files. I am dumping everything to the console and when the two threads are taking the tasks from the commands file they take the proper one's they don't have the same task but after Thread-1 woke up it have the Thread-2 task. Can someone help me to understand how to use wait and notify for this case and in any other of course. I have read Java Concurrency in practice but they have only put and take methods there with wait and notify also the examples in google are naive and they work as expected. Thanks in advance.

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

The problem with mutual wait-notify calls in your case is the fact that that one thread can finish its job and invoke notify before another thread invokes wait . Notification is not "remembered" so if notify is called before actual wait is, then waiting thread will wait forever (well, until another notify).

If I am not mistaken, you want T1 has to do some job, wait for some notification from T2 and then do some other + exit. If this is correct, it will be much simpler for you to use CountDownLatch or Semaphore

Both can cancel effect of racing conditions mentioned above. Countdown latch can be "counted down" when other threads are waiting for it to do so, or prior that which will result in "waiting thread" to never have to wait as "the door is already opened".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM