Java 使用WatchService设置发布服务器订阅服务器

Java 使用WatchService设置发布服务器订阅服务器,java,multithreading,Java,Multithreading,我正在尝试使用NIO中的WatchService设置一个双向发布服务器订阅服务器 我对线程没有太多的经验,所以如果我没有任何意义,请随时给我打电话 这只是一个示例,用于了解库是如何工作的,但生产代码将侦听输入文件中的更改,当文件更改时,它将进行一些计算,然后写入输出文件。此输出文件将由另一个程序读取,并在其上运行一些计算。然后将写入输入文件,循环继续 不过,对于这个测试,我使用watchers创建了两个线程,第一个线程监听first.txt并写入second.txt,第二个线程等待second.

我正在尝试使用NIO中的WatchService设置一个双向发布服务器订阅服务器

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

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

不过,对于这个测试,我使用watchers创建了两个线程,第一个线程监听first.txt并写入second.txt,第二个线程等待second.txt并写入first.txt。我所做的就是增加一个count变量并写入每个线程的输出文件。这两个线程都对它们真正关心的文件进行了阻塞调用和筛选,所以我认为这种行为应该是这样的

两个线程都在等待接收呼叫。 更改first.txt以启动流程 这会触发第一个线程更改second.txt 然后触发第二个线程更改first.txt 等等

至少我希望如此。最终的结果是线程不同步,当我对计数达到1000时,一个线程通常落后50多个点

这是观察者的代码

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

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

该工具似乎非常正确,但您的代码必须按顺序流动,否则一切都将失败正如你所注意到的那样。 将其视为一个必须在另一个事务开始之前完成的事务。 在这种情况下,事务可以归结为 1.检测文件1更改 2.修改文件2 3.检测文件2更改 4.修改文件1

所以在这个循环完全结束之前,如果另一个循环开始,那么就会有麻烦。当您使用线程时,调度和执行不是完全可预测的,因此您不知道2个线程在做什么。 他们是否按照您的要求按顺序做事。 因此,您必须为任何人共享您的线程代码以提供特定的 解决方案

另一点是,您是否可以保留一个包含更改的小更改文件 并使用它,而不是使用更大的生产文件。那个
您可以将焦点缩小到更小的对象。

在运行代码后,我注意到了一些更具体的内容。 这很好…尽管有几点…没有必要这样做 螺纹1.连接; 螺纹2.连接; 这两个线程都需要并发运行,因此不需要连接 需要。 对于你的主要问题…线程不同步,因为它们 连接到自己的观察者对象,因此计数值 对于这两个线程来说是不同的。 因此,取决于调度程序运行线程的方式…其中一个线程 将获得更多里程数,并将首先达到计数1000,而 其他国家仍然落后

我正在编辑以回应您的评论…Take是一个阻止呼叫,并且 它工作得很好。在我的例子中,唯一被捕获的事件是ENTRY\u MODIFY 因此,没有多事件问题。 一个技巧是您可以设置dir.registerwatcher,ENTRY\u MODIFY;编码 仅检查修改事件。请看下面我的代码。还有 我的println可能有助于更好地理解代码流

公共课堂观察测试{

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

但我认为这是一个阻拦电话。在for循环下的take和filter之间,如果有一个写事件,那么它应该根本无法前进。注意,有时写操作会触发多个事件。只是有时候。烦人的
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();
    }