Multithreaded Java

I am trying to implement multithreading in a Java Mandelbrot application:

This is what I have so far:

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

public class MandelbrotSet {
   private int numberOfIterations;
   private double realMin;
   private double realMax;
   private double imaginaryMin;
   private double imaginaryMax;
   private int width;
   private int height;
   public BufferedImage image;
   public Graphics2D imageGraphics;

   public MandelbrotSet() {
      // Set the width and the height
      this.width = 600;
      this.height = 400;
      image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
      imageGraphics = image.createGraphics();
                this.realMin = -2.0;
      this.realMax = 1;
      this.imaginaryMin = -1;
      this.imaginaryMax = 1;
      this.numberOfIterations = 1000;
   }

   public Complex calculateComplexNumber(int x, int y) {
      double realPart = realMin + x * (realMax - realMin) / (this.getWidth() - 1);
      double imaginaryPart = imaginaryMax - y * (imaginaryMax - imaginaryMin) / (this.getHeight() - 1);

      return new Complex(realPart, imaginaryPart);
   }

   public void calculateMandelbrotImagePoints() {
      Thread[] threads = new Thread[4];

      for (int i = 0; i < maxThreads; i++) {
         threads[i] = new Thread(new MThread(i));
         threads[i].start();
      }
   }

   class MThread implements Runnable {
      private int i;

      public MThread(int i) {
         this.i = i;
      }

      //Method uses the thread number to draw the mandelbrot in columns
      public void run() {
         for (int x = i; x < width; x += 4) {
            for (int y = 0; y < height; y++) {
               int n = 0;
               Complex c = calculateComplexNumber(x, y);
               Complex z = c;

               while ((zNumber.modulusSquared() < 4.0D) && (n < numberOfIterations)) {
                  z = z.square();
                  z.add(c);
                  n++;
               }

               if (n == numberOfIterations) {
                  imageGraphics.setColor(Color.BLACK);
               } else {
                  imageGraphics.setColor(Color.getHSBColor(n / 100.0F, 1, 1));
               }
               imageGraphics.drawLine(x,y,x,y);
            }
         }
      }
   }
}

      

The problem that comes up is that when drawing the image, the wrong pixels are displayed on the image:

http://i.stack.imgur.com/wq2TN.png

When I check the stream with something like:

threads[i].isAlive();

      

the image appears to be rendered successfully, however the image takes much longer (up to 3 times) to render.

Two things that I was interested in.

  • Where am I going wrong?

  • What would be the best way to draw Mandelbrots in BufferedImage

    for a large number of iterations (> 1000)?

+3


source to share


4 answers


I expect this to be what Michael Chang suggested. I have adjusted the code to render in stripes.

Please note that I was unable to test this. I am not familiar with Java graphics.



                                                                                           import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

public class MandelbrotSet {
  private int numberOfIterations;
  private double realMin;
  private double realMax;
  private double imaginaryMin;
  private double imaginaryMax;
  private int width;
  private int height;
  public BufferedImage image;
  public Graphics2D imageGraphics;
  static final int nThreads = 4;

  public MandelbrotSet(int width, int height) {
    // Set the width and the height
    this.width = width;
    this.height = height;
    image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
    imageGraphics = image.createGraphics();
    this.realMin = -2.0;
    this.realMax = 1;
    this.imaginaryMin = -1;
    this.imaginaryMax = 1;
    this.numberOfIterations = 1000;
  }

  public Complex calculateComplexNumber(int x, int y) {
    double realPart = realMin + x * (realMax - realMin) / (width - 1);
    double imaginaryPart = imaginaryMax - y * (imaginaryMax - imaginaryMin) / (height - 1);

    return new Complex(realPart, imaginaryPart);
  }

  public void calculateMandelbrotImagePoints() {
    Thread[] threads = new Thread[nThreads];
    int bandHeight = height / nThreads;

    for (int i = 0; i < nThreads; i++) {
      BufferedImage band = new BufferedImage(width, bandHeight, BufferedImage.TYPE_4BYTE_ABGR);
      threads[i] = new Thread(new MThread(band, i * bandHeight, bandHeight));
      threads[i].start();
    }
  }

  class MThread implements Runnable {
    final BufferedImage band;
    final Graphics2D g;
    final int top;
    final int height;

    private MThread(BufferedImage band, int top, int height) {
      this.band = band;
      g = band.createGraphics();
      this.top = top;
      this.height = height;
    }

    @Override
    public void run() {
      for (int x = 0; x < width; x++) {
        for (int y = top; y < top + height; y++) {
          int n = 0;
          Complex c = calculateComplexNumber(x, y);
          Complex z = c;

          while ((z.times(z).mod() < 4.0D) && (n < numberOfIterations)) {
            z = z.times(z).plus(c);
            n++;
          }

          if (n == numberOfIterations) {
            g.setColor(Color.BLACK);
          } else {
            g.setColor(Color.getHSBColor(n / 100.0F, 1, 1));
          }
          g.drawLine(x, y-top, x, y-top);
        }
      }
      // Do somehing to merge this band ino the main one.
      // Not familiar with java graphics so this may be wrong.
      imageGraphics.drawImage(band, null, 0, top);
    }
  }

}

      

+2


source


Drawing is not thread safe, so it is not possible to draw to the same {screen, image, any} of several threads. It is possible that your threads will be interrupted (i.e., context transition is possible) between these lines:

                  imageGraphics.setColor(Color.getHSBColor(n / 100.0F, 1, 1));
               }
               imageGraphics.drawLine(x,y,x,y);

      



One option would be to give each thread its own image (such as a quarter image as a tile) to draw, and then draw the images together at the end.

+4


source


Drawing is not thread safe - one thread can redraw the results of another thread.

You can create a two-dimensional array of results with the volatile keyword representing the resulting pixels. Themes can be saved in this array without conflics and when the array is full (threads are complete, you can use the .join () method) you draw everything at once.

+1


source


To fix the problem, change

if (n == numberOfIterations) { 
  imageGraphics.setColor(Color.BLACK); 
} else { 
  imageGraphics.setColor(Color.getHSBColor(n / 100.0F, 1, 1)); 
} 
imageGraphics.drawLine(x,y,x,y); 

      

in

synchronized(MandelbrotSet.this) {
   if (n == numberOfIterations) { 
     imageGraphics.setColor(Color.BLACK); 
   } else { 
     imageGraphics.setColor(Color.getHSBColor(n / 100.0F, 1, 1)); 
   } 
  imageGraphics.drawLine(x,y,x,y); 
}

      

This will prevent thread collisions when updating the image, but will still improve the parallel performance you are looking for on multi-core systems.

+1


source







All Articles