猿问

多线程(即基于线程池)Java 应用程序中的损坏结果

我正在尝试 Java 中的多线程,更具体地说,是线程池。作为测试,我编写了一个应用程序,该应用程序仅使用多线程来更改图像的颜色以提高速度。然而,由于某种我不知道的原因,根据我如何设置此测试,我得到的结果已损坏。下面我将描述测试应用程序如何与完整的源代码一起工作。

非常欢迎任何帮助!谢谢你!

测试应用

我有一个 400x300 像素的图像缓冲区,用深蓝色初始化,如下所示:

程序必须用红色完全填充它。

尽管我可以简单地循环所有像素,并按顺序将每个像素着色为红色,但为了提高性能,我决定利用并行性。因此,我决定用单独的线程填充每个图像行。由于行数(300 行)比可用 CPU 核心数大得多,因此我创建了一个线程池(包含 4 个线程),它将消耗 300 个任务(每个任务负责填充一行)。

该计划的组织如下:

  • RGB 类:将像素颜色保存在双精度数 3 元组中。

  • RenderTask 类:用红色填充图像缓冲区的给定行。

  • 渲染器类:

    • 创建图像缓冲区。

    • 使用“newFixedThreadPool”创建线程池。

    • 创建 300 个任务供线程池使用。

    • 完成线程池服务。

    • 将图像缓冲区写入 PPM 文件。

一切似乎都适用于该代码,并且我得到了预期的红色图像缓冲区,如下所示:

https://img4.mukewang.com/6513e7150001ee5f03950298.jpg

问题


但是,如果我修改 RenderTask.run() 方法,使其按顺序多次重复设置同一缓冲区位置的颜色,如下所示(我将其称为Version 2):


    @Override

    public void run() {   

        for(int column = 0; column < row_width; ++column) {

            for(int s = 0; s < 256; ++s) {


                image_buffer[current_row][column] =  new RGB(1.0, 0.0, 0.0);


            }

        }

    }

然后我得到以下损坏的图像缓冲区:

https://img4.mukewang.com/6513e7260001632e03990298.jpg

实际上,每次运行程序的结果都不同,但总是损坏。

据我了解,没有两个线程同时写入同一内存位置,因此似乎没有出现竞争情况。

即使在我认为不会发生的“错误共享”的情况下,我也期望只会降低性能,而不是损坏结果。

因此,即使有冗余的分配,我也希望得到正确的结果(即完全红色的图像缓冲区)。

所以,我的问题是:如果程序的版本 2 与版本 1 的唯一区别是赋值操作在线程范围内冗余执行,为什么会发生这种情况?

是否会出现某些线程在完成之前就被销毁的情况?这会是 JVM 中的错误吗?或者我错过了一些微不足道的事情?(最有力的假设:)

感谢你们!!


绝地无双
浏览 80回答 2
2回答

撒科打诨

ExecutorService.shutdown() 不会等待其所拥有的任务终止,它只会停止接受新任务。调用 shutdown 后,如果您想等待执行程序服务完成,则应该在执行程序服务上调用 waitTermination。因此,当您开始将图像写入文件时,所有任务尚未完成执行。

宝慕林4294392

要添加答案,您可以使用以下代码来关闭线程池以下方法分两个阶段关闭 ExecutorService,首先调用 shutdown 拒绝传入任务,然后调用 shutdownNow(如有必要)取消任何延迟任务:void shutdownAndAwaitTermination(ExecutorService pool) {  pool.shutdown(); // Disable new tasks from being submitted  try {    // Wait a while for existing tasks to terminate    if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {      pool.shutdownNow(); // Cancel currently executing tasks      // Wait a while for tasks to respond to being cancelled      if (!pool.awaitTermination(60, TimeUnit.SECONDS))          System.err.println("Pool did not terminate");    }  } catch (InterruptedException ie) {    // (Re-)Cancel if current thread also interrupted    pool.shutdownNow();    // Preserve interrupt status    Thread.currentThread().interrupt();  }}
随时随地看视频慕课网APP

相关分类

Java
我要回答