使用WatchService

时间:2015-05-05 19:20:15

标签: java multithreading

我正在尝试使用NIO中的WatchService设置双向发布者订阅者。

我对线程没有太多经验,所以如果我没有任何意义,请随时给我打电话!

这只是一个了解库如何工作的示例,但生产代码将监听输入文件中的更改,当文件更改时,它将执行一些计算,然后写入输出文件。此输出文件将由另一个程序读取,将在其上运行一些计算。然后将输入文件写入并继续循环。

对于此测试,我正在与观察者进行2个线程,第一个线程侦听first.txt并写入second.txt,第二个线程等待second.txt并写入{ {1}}。我正在做的就是递增计数变量并写入每个线程的输出文件。这两个线程都有阻塞调用和过滤器实际关心的文件,所以我认为行为看起来像

两个线程都在等待take()调用。 更改first.txt以开始此过程 这会触发第一个线程更改first.txt 然后触发第二个线程更改second.txt 等等。

或者我希望如此。最终的结果是线程失去了同步,当我这样做时计数达到1000,一个线程通常落后超过50个点。

以下是观察者的代码

first.txt

我不想阅读文件来决定文件是否已更改,因为生产中的这些文件可能很大。

我觉得这可能太复杂了,我正试图用截肢来处理刮伤的膝盖。我错误地使用此库吗?我是否使用了错误的工具来完成这项工作,如果有的话,我可以使用其他任何文件监听库,这样我就不必为上次编辑进行轮询了吗?

编辑: 哎呀,这是我写的测试,它设置了两个线程

Watcher(Path input, Path output) throws IOException {
    this.watcher = FileSystems.getDefault().newWatchService();
    this.input = input;
    this.output = output;
    dir = input.getParent();
    dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
}

void watchAndRespond() throws IOException, InterruptedException {

    while (count < 1000) {

        WatchKey key = watcher.take();

        for (WatchEvent<?> event: key.pollEvents()) {
            if (! event.context().equals(input.getFileName())) {
                continue;
            }

            WatchEvent.Kind kind = event.kind();
            if (kind == OVERFLOW) {
                continue;
            }

            count++;

            try (BufferedWriter out = new BufferedWriter(new FileWriter(output.toFile()))) {
                out.write(count + "");
            }
        }
        key.reset();
    }
}

我认为问题在于某些修改会将多个事件放入队列中,此时我无法辨别它们是否是由一次保存或两次单独保存创建的2个事件。

2 个答案:

答案 0 :(得分:0)

该工具似乎非常正确,但您的代码必须按顺序流动,否则一切都会因为您注意到而不同步。 将其视为必须在另一个事务开始之前完成的事务。 在这种情况下,交易可以归结为 1.)检测File1更改 2.)修改File2 3.)检测File2更改 4.)修改File1

所以在此循环完全结束之前,如果另一个循环开始,则会出现问题。当您使用线程时,调度和执行并不完全可预测,因此您不会知道您的2个线程在做什么。 他们是否按照您的要求按顺序执行。 所以你必须分享你的线程代码给任何人给出一个特定的 溶液

另一点是你可以保留一个包含更改的小变更文件 并使用它而不是使用更大的生产文件。那 你可以将焦点减少到一个较小的物体。

答案 1 :(得分:0)

这是我在运行代码后注意到的更具体的内容。 它很好......虽然有些要点......没有必要 thread1.join(); thread2.join(); 两个线程都需要并发运行,因此不能连接 需要。 对于你的主要问题......线程因为它们不同步 连接到他们自己的观察者对象,所以计数值 两个线程都不同。 所以取决于调度程序如何运行线程......其中之一 将获得更多里程,并将首先达到1000 其他人仍然落后。

我正在编辑以回应您的评论.... Take是一个阻止电话和 它工作得很好。在我的情况下,唯一被困的事件是ENTRY_MODIFY 所以没有多重事件的问题。 一个提示是你可以设置dir.register(watcher,ENTRY_MODIFY);在代码中 仅检查修改事件。请参阅下面的代码..也 我的printlns可能有助于更好地理解代码流。

公共类WatcherTest {

public static void main(String args[]) throws InterruptedException, IOException {

    Path input =  FileSystems.getDefault().getPath("txt", "input.txt");
    Path output =  FileSystems.getDefault().getPath("txt", "output.txt");

    if (Files.exists(input)) {
        Files.delete(input);
    }
    if (Files.exists(output)) {
        Files.delete(output);
    }

    Thread thread1 = new Thread(new WatchFileTask(input, output ), "ThreadToOpt");
    Thread thread2 = new Thread(new WatchFileTask(output, input ), "ThreadToInpt");

    thread1.start();
    thread2.start();

    Thread.sleep(100);

    BufferedWriter out = new BufferedWriter(new FileWriter(input.toFile()));
    out.write(0 + "");
    out.close();

    //thread1.join();
    //thread2.join();

    //int inputResult = Integer.parseInt(Files.readAllLines(input, Charset.defaultCharset()).get(0));
    // int outputResult = Integer.parseInt(Files.readAllLines(output, Charset.defaultCharset()).get(0));

}

}

类FileWatcherService {

private WatchService watcher;
private Path input;
private Path output;
private Path dir;

private   int count = 0;

FileWatcherService(Path input, Path output) throws IOException {
    this.watcher = FileSystems.getDefault().newWatchService();
    this.input = input;
    this.output = output;
    Path dir = input.getParent();
    dir.register(watcher, ENTRY_MODIFY);
}


void watchAndRespond() throws IOException, InterruptedException {

    while (count < 1000) {


        System.out.println("\n COUNT IS " + count + " in Thread " +  Thread.currentThread().getName());

        System.out.println("\n Blocking on Take in Thread " + Thread.currentThread().getName());
        WatchKey key = watcher.take();

        System.out.println("\n Out of Blocking State " + Thread.currentThread().getName());

        int eventsPassed = 0;
        for (WatchEvent<?> event: key.pollEvents()) {

            if (!event.context().equals(input.getFileName())) {
                continue;
            }
            System.out.println("\n File Context : " + event.context() + " Event Kind " + event.kind() + " in Thread " + Thread.currentThread().getName());
            WatchEvent.Kind kind = event.kind();


            if (kind == OVERFLOW) {
                continue;
            }

            eventsPassed++;
            count++;
            //synchronized(output){

                try (BufferedWriter out = new BufferedWriter(new FileWriter(output.toFile()))) {
                    out.write(count + "");
                    System.out.println("\n Wrote count : " +  count + " to File " + output.getFileName() + " in Thread " + Thread.currentThread().getName());
                }
        //  }
        }
        System.out.println("\n The eventsPassed counter is " + eventsPassed + " \n for thread  " + Thread.currentThread().getName());

        key.reset();
    }

} }