I am experimenting with multithreading in Java, more specifically, threadpools. As a test, I have written an application that simply changes the color of an image using multithreading for speed. However, for some reason unknown to me, I get corrupted results depending on how I set up this test. Below I describe how the test application works together with the complete source code.
Any help is very welcome! Thank you!
The Test Application
I have a 400x300 pixel image buffer that is initialized with the dark blue color, as shown below:
The program must fill it up completely with the red color.
Although I could simply loop over all pixels, coloring each one sequentially with red, I've decided, for performance, to take advantage of parallelism. Thus, I've decided to fill each image row with a separate thread. Since the number of rows (300 rows) is much larger then the number of available CPU cores, I've created a threadpool (containing 4 threads) that will consume 300 tasks (each one in charge of filling up one row).
The program is organized as follows:
- RGB class: holds the pixel color in a 3-tuple of doubles.
- RenderTask class: fills up a given row of the image buffer with the red color.
- Renderer class:
- creates the image buffer.
- creates the threadpool with "newFixedThreadPool".
- creates 300 tasks to be consumed by the threadpool.
- finishes the threadpool service.
- writes the image buffer to a PPM file.
Below you may find the complete source code (I will call this code Version 1):
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.io.*;
class RGB {
RGB() {}
RGB(double r, double g, double b) {
this.r = r;
this.g = g;
this.b = b;
}
double r;
double g;
double b;
}
class RenderTask implements Runnable {
RenderTask(RGB[][] image_buffer, int row_width, int current_row) {
this.image_buffer = image_buffer;
this.row_width = row_width;
this.current_row = current_row;
}
@Override
public void run() {
for(int column = 0; column < row_width; ++column) {
image_buffer[current_row][column] = new RGB(1.0, 0.0, 0.0);
}
}
RGB[][] image_buffer;
int row_width;
int current_row;
}
public class Renderer {
public static void main(String[] str) {
int image_width = 400;
int image_height = 300;
// Creates a 400x300 pixel image buffer, where each pixel is RGB triple of doubles,
// and initializes the image buffer with a dark blue color.
RGB[][] image_buffer = new RGB[image_height][image_width];
for(int row = 0; row < image_height; ++row)
for(int column = 0; column < image_width; ++column)
image_buffer[row][column] = new RGB(0.0, 0.0, 0.2); // dark blue
// Creates a threadpool containing four threads
ExecutorService executor_service = Executors.newFixedThreadPool(4);
// Creates 300 tasks to be consumed by the threadpool:
// Each task will be in charge of filling one line of the image buffer.
for(int row = 0; row < image_height; ++row)
executor_service.submit(new RenderTask(image_buffer, image_width, row));
executor_service.shutdown();
// Saves the image buffer to a PPM file in ASCII format
try (FileWriter fwriter = new FileWriter("image.ppm");
BufferedWriter bwriter = new BufferedWriter(fwriter)) {
bwriter.write("P3\n" + image_width + " " + image_height + "\n" + 255 + "\n");
for(int row = 0; row < image_height; ++row)
for(int column = 0; column < image_width; ++column) {
int r = (int) (image_buffer[row][column].r * 255.0);
int g = (int) (image_buffer[row][column].g * 255.0);
int b = (int) (image_buffer[row][column].b * 255.0);
bwriter.write(r + " " + g + " " + b + " ");
}
} catch (IOException e) {
System.err.format("IOException: %s%n", e);
}
}
}
Everything seems to be working with that code, and I get the expected red image buffer, as shown below:
The Problem
However, if I modify the RenderTask.run() method such that it re-sets redundantly the color of the same buffer position several times in sequence, as shown below (I will call this one 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);
}
}
}
Then I get the following corrupted image buffer:
Actually, the result is different each time I run the program, but always corrupted.
As far as I understand it, no two threads are writing to the same memory position simultaneously, so it seems that there is no racing condition in sight.
Even in the case of "false sharing", which I don't think is happening, I would expect only lower performance, not corrupted results.
Thus, even with redundant assignments, I would expect to get the correct result (i.e. a completely red image buffer).
So, my questions are: Why is this happening to the Version 2 of the program if the only difference with respect to Version 1 is that the assignment operation is being executed redundantly within the scope of the thread?
Would it be the case that some threads are being destroyed before they finish? Would it be a bug in the JVM? Or have I missed something trivial? (the strongest hypothesis :)
Thank you guys!!


