我试图理解Java多线程,但是在使该线程完成其运行方法方面遇到了一些麻烦。

我有两个工作线程正在消耗阻塞队列中的消息。他们应该对传递的值做一些简单的操作,并将结果附加到文件中。因为我不想为每个I/O操作打开文件读取器,所以我想使用StringBuilder逐步构建报告,并在线程从主循环中退出时将其全部写入。但是,循环后的代码似乎从未运行过。

这是在线程中运行的代码:

@Override
public void run() {

    StringBuilder builder = new StringBuilder();

    while(true) {

        if(!shouldRun) break;

        try {
            WorkMessage msg = (WorkMessage) ((BlockingQueue) inputQueue).take();

            int payload = msg.payload;
            LocalTime time = msg.timeCreated;

            String report = "Adder got message at: " + time + ". Result: " + (payload + toAdd);

            builder.append(report);

        } catch (InterruptedException ie) {
            System.out.println("ERROR IN ADDER: " + ie.getMessage());
        }
    }
    writeToReportFile(builder.toString());
}
writeToReportFile方法没有被调用。当我在控制线程中将shouldRun设置为false时,似乎在while循环结束后跳过了代码。我没有直接从外部线程将shouldRun设置为false,而是从外部调用了内部kill()方法,该方法在内部将shouldRun设置为false。

有人知道为什么会这样吗?我确定我只是在这里遗漏了一些简单的东西。

最佳答案

您应该使用Thread#interrupt来终止线程,您的手动标志方法会给您带来问题,因为对shouldRun标志的更新不可见,并且队列无法停止阻塞。

如果您的控制线程在Worker对象上调用kill方法,则该线程将设置shouldRun标志。需要使用volatile使它可见,以便工作线程(正在执行run方法)可以看到它。

无需使用volatile(或使用AtomicBoolean或其他方法使此更改在线程间可见),JVM可以自由地将变量的内容保留在其他线程不可见的处理器本地缓存中,或优化代码中的检查您的工作线程正在运行。

使用并发类(例如队列)的线程需要使用中断而不是依靠手动滚动标志,以便对它们所包含的组件(例如队列)的操作可以响应取消请求。否则,您的队列将无法得知您的手动滚动标志,因此它无法停止响应对kill方法的调用而阻塞。

09-26 22:10
查看更多