This lab explores more advanced concepts of multithreaded programming, including thread-safe classes and thread pooling.
Oracle Java Tutorial links
- Use java.util.concurrent collections.
- Apply atomic variables and locks.
- Use Executors and ThreadPools.
- Use the parallel Fork/Join Framework.

The Mandelbrot set (Wikipedia entry) is a complex number set frequently used as an example of fractal rendering. We are going to use a Mandelbrot set generator to highlight some concurrency issues with multithreaded programming.
Lets start by creating a basic Mandelbrot set generator. This will take values on the complex \( c = x + iy \) plane, test each for membership of the Mandelbrot set, and assign a colour to the matching pixel based on the number of iterations of the function \(z_{n+1} = z_n^2 + c \) it takes for the value \(z\) to exceed a given threshold.
Create a class BasicMandelbrot in the package concurrency with the following method:
// return number of iterations to check if c = x + iy is in Mandelbrot set public static int mandelbrot(double x0, double y0, int maxIterations) { double zx = x0; double zy = y0; for (int t = 0; t < maxIterations; t++) { if (zx * zx + zy * zy > 4.0) { return t; } double tmp = zx * zx - zy * zy + x0; zy = 2.0 * zx * zy + y0; zx = tmp; } return maxIterations; }
Create a constructor which sets up an image canvas. Note, we're using Java graphics here rather than StdDraw for efficiency.
// Determine the position and zoom level of the image of the Mandelbrot set private static final double X_CENTRE = 0; private static final double Y_CENTRE = 0.75; private static final double MANDELBROT_SIZE = 1; public static final double X_START = X_CENTRE - MANDELBROT_SIZE/2; public static final double Y_START = Y_CENTRE - MANDELBROT_SIZE/2; // maximum number of iterations public static final int ITERATION_MAX = 255; public static final int IMAGE_SIZE = 800; public static final double SCALE = MANDELBROT_SIZE/IMAGE_SIZE; public JFrame frame; public BufferedImage image; public Graphics2D graphics; public BasicMandelbrot() { // Set up the graphics to display the image frame = new JFrame("Mandelbrot"); image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_RGB); graphics = image.createGraphics(); graphics.setColor(Color.WHITE); graphics.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE); ImageIcon icon = new ImageIcon(image); JLabel draw = new JLabel(icon); frame.setContentPane(draw); frame.setResizable(false); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); graphics.scale(1/SCALE, 1/SCALE); graphics.translate(-X_START, -Y_START); }
Create a run method which determines the colour of each pixel using the mandelbrot method.
public void run() { for (int i = 0; i < IMAGE_SIZE; i++) { for (int j = 0; j < IMAGE_SIZE; j++) { double x0 = X_START + SCALE * i; double y0 = Y_START + SCALE * j; int gray = ITERATION_MAX - mandelbrot(x0, y0, ITERATION_MAX); Color color = new Color(gray, gray, gray); graphics.setColor(color); graphics.fill(new Rectangle2D.Double(x0, y0, SCALE, SCALE)); frame.repaint(); } } }
Finally create a main method which creates an instance of the class and calls the run method.
public static void main(String[] args) { new BasicMandelbrot().run(); }
The area of the Mandelbrot set displayed is given by the values of X_CENTRE, Y_CENTRE, MANDELBROT_SIZE. Try running the code - you should see an image like that above.

Next, create a new class in package concurrency called BlockedMandelbrot. This class should produce the same output as BasicMandelbrot, but should create and use the method
public void plotBlock(double xStart, double yStart, int blockSize, double scale, int iterationMax)
This method will plot a blockSize by blockSize square piece of the image starting at position (xStart, yStart) on the image canvas, with the parameters iterationMax and scale defined as in BasicMandelbrot.
Modify your run method to call plotBlock instead of plotting each pixel individually. Running your code should produce the same image of the Mandelbrot set as before.

Now that you have a program that can render the image in independent blocks, lets try using multiple threads. Create the class ThreadedMandelbrot in the same package. Modify the run method to run each plotBlock in its own thread. Run the code. You are likely to see a result similar to this image.
Something has gone wrong. What ? Try to figure it out before continuing.
As you have discovered, having multiple threads interact with the same objects simultaneously can be risky. There are a number of strategies discussed in the Java tutorials above to protect against concurrency problems.
The simplest in this case is to simply synchronise critical sections of the code, ensuring a block of code runs without disruption by other threads. Try using the synchronisation options discussed in the Java tutorial on your class. See what options fix the image generation, and what effect they have on image generation speed.
An automated test has been created for this exercise: ThreadedMandelbrotTest.java (in package concurrency).
For the test to execute correctly, the ThreadedMandelbrot.run method should only terminate after all the block threads have terminated.