首页 > 解决方案 > 如何正确使用条件队列

问题描述

我有一个包含“任务”对象列表的 taskList 对象“持有者”。在一个循环中,我从文本文件中读取命令,并在每一行上添加带有命令的新任务对象并将其添加到“持有人”对象中。命令的格式为“cat source_file1 source_file2 output_file”,如果其中一个文件丢失,我希望线程等待,然后继续执行新命令。我正在尝试使用具有以下谓词的条件队列(如果命令的两个文件都存在,则唤醒并完成工作)直到睡眠。当第一个线程进入等待状态时,我正在使用两个线程进行测试,程序永远挂起,并且它没有让其他线程有机会生成等待线程丢失的文件。

public class MyTaskManager {

    public static void main(String[] args) {
        List<Thread> threads = new ArrayList<>();
        TaskHolder holder = new TaskHolder();

        for (int i = 0; i < 1; i++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    BufferedReader br = null;
                    try {
                        br = new BufferedReader(new InputStreamReader(
                                new FileInputStream("commands.txt")));
                        String line = "";
                        while  ( ( line = br.readLine() ) != null) {
                            String[] in = line.split(" ");
                            File dep0 = new File(in[1]), dep1 = new File(in[2]);

                            holder.addTask(new Task(dep0, dep1));

                            synchronized (holder) {
                                System.out.println(Thread.currentThread().getName());
                                while (!dep0.exists() || !dep1.exists()) {
                                    holder.wait();
                                }


                            }
                            Process process = Runtime.getRuntime()
                                    .exec(new String[]{"bash", "-c", in[0] + " " + in[1] + " " + in[2]});
                            process.getInputStream().transferTo(new FileOutputStream(in[3]));

                            holder.notifyAll();
                        }
                    } catch (IOException | InterruptedException e) {
                        Thread.currentThread().interrupt();
                        e.printStackTrace();
                    }
                }
            });
            threads.add(t);
        }

        for (int i = 0; i < 1; i++)
            threads.get(i).start();


        for (int i = 0; i < 1; i++) {
            try {
                threads.get(i).join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

public class TaskHolder {
    private List<Task> taskList;

    TaskHolder() {
        taskList = new ArrayList<>();
    }

    void addTask(Task task) {
        taskList.add(task);
    }

    List<Task> getTaskList() {
        return taskList;
    }
}

标签: javamultithreading

解决方案


您没有调用notify. 此外,holder您正在同步的内容实际上并不能保护任何东西。您必须使用锁来保护您正在等待更改的共享状态。

听起来您不了解条件的意义,也不了解它们的工作原理。简单地说,它们提供了一个原子的“解锁并等待”操作来解决以下问题:

  1. 要检查某些共享状态,我们必须持有一个锁。
  2. 要等待共享状态改变,我们必须释放锁,以便另一个线程可以改变共享状态。

这将允许没有原子“解锁和等待”操作的死锁。这就是wait目的 - 如果您解锁然后等待,它是一个原子的“解锁并等待”以避免竞争条件。如果这不是您遇到的问题,那么wait就不是您需要的解决方案!

这不是你遇到的问题。所以这些不是你要找的机器人。不要使用synchronized- 它用于线程之间的通信。并且不要使用wait,它用于当您需要原子“解锁并等待”操作以避免竞争条件时。


推荐阅读