Java多线程:线程不会完成run()方法

Java多线程:线程不会完成run()方法,java,multithreading,Java,Multithreading,我试图理解java多线程,但在让这个线程完成其run方法时遇到了一些问题 我有两个工作线程使用阻塞队列中的消息。他们应该对传递的值做一些简单的操作,并将结果附加到文件中。因为我不想为每个I/O操作打开一个文件读取器,所以我想使用StringBuilder逐步构建报告,并在线程被踢出主循环时编写报告。然而,循环后的代码似乎从未运行过 以下是线程中运行的代码: @Override public void run() { StringBuilder builder = new StringB

我试图理解java多线程,但在让这个线程完成其run方法时遇到了一些问题

我有两个工作线程使用阻塞队列中的消息。他们应该对传递的值做一些简单的操作,并将结果附加到文件中。因为我不想为每个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
方法是未调用的方法。当我在控制线程中将
shouldlrun
设置为false时,它似乎会在while循环结束后跳过代码。我没有直接从外部线程将
shouldlrun
设置为false,而是从外部调用一个内部
kill()
方法,该方法在内部将
shouldlrun
设置为false

有人知道为什么会这样吗?我确信我只是缺少了一些简单的东西。

你应该使用线程中断来终止你的线程,你的手动滚动标志方法给你带来了问题,因为更新到shouldRun标志不可见,队列无法停止阻塞

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

如果不使用volatile(或者使用AtomicBoolean或其他方法使此更改在线程之间可见),JVM可以自由地将处理器本地缓存中的变量内容保持为其他线程不可见,或者优化工作线程正在运行的代码中的签出


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

线程很可能由于take()而被阻塞。为什么不使用poll(time,timeunit)方法测试这段代码呢?poll(time,timeunit)方法将等待指定的时间,然后返回,因此在等待指定的时间后,它将检查您的标志shouldlrun,并将退出循环

资料来源:

另外,请声明变量应作为volatile运行。 原因:以下摘录摘自《实践中的Java并发》一书:

服务器JVM比客户端JVM执行更多的优化,例如从循环中提升未在循环中修改的变量


所以,在使用服务器JVM的环境中,您可能会在无限循环中结束,因为shouldRun变量不会在循环中修改。

shouldRun是否为volatile?不,但我认为我不需要将其设置为volatile,因为我没有直接从控制线程设置它。我正在使用该线程类中的一个方法(由控制线程调用以执行终止)来更改它方法是一个阻塞操作,因此即使更改标志,如果(!shouldRun)中断,代码也永远不会到达
如果从未收到
工作消息
。对于您发布的代码段,唯一的方法是从类中更改shouldRun标志,导致
中断异常
,关闭通道等,并杀死它所在的作业。谢谢