Java multihilo

Estoy tratando de implementar subprocesos múltiples en mi aplicación Java Mandelbrot:

Esto es lo que tengo hasta ahora:

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); } } } } } 

El problema que está ocurriendo es que cuando se dibuja la imagen, se muestran píxeles incorrectos en la imagen:

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

Cuando reviso un hilo con algo como:

 threads[i].isAlive(); 

La imagen parece mostrarse correctamente, sin embargo, la imagen tarda mucho más tiempo (hasta 3 veces más) en renderizarse.

Dos cosas que me preguntaba.

  1. ¿A dónde me voy mal?

  2. ¿Cuál sería la mejor manera de dibujar Mandelbrots en una imagen BufferedImage en BufferedImage para un gran número de iteraciones (> 1000)?

Supongo que esto es lo que sugirió @Michael Chang. He ajustado el código para renderizar en bandas.

Tenga en cuenta que no he podido probar esto. No estoy familiarizado con los gráficos de Java.

  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); } } } 

El dibujo no es seguro para subprocesos, por lo que no es posible dibujar a la misma {pantalla, imagen, lo que sea} desde varios subprocesos. Es posible que sus hilos sean interrumpidos (es decir, es posible que ocurra un cambio de contexto) entre estas líneas:

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

Una opción sería dar a cada hilo su propia imagen (por ejemplo, un cuarto de la imagen como un mosaico) para dibujar, y luego unir las imágenes al final.

El dibujo no es seguro para el hilo: un hilo puede volver a pintar los resultados de otro hilo.

Puede crear una matriz bidimensional de resultados con palabras clave volátiles, que representan los píxeles resultantes. Los subprocesos se pueden guardar en esta matriz sin conflics y cuando la matriz se llena (los subprocesos terminados, puede usar el método .join ()), pinta todo a la vez.

Para solucionar el problema, cambiar

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

a:

 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); } 

Esto evitará las colisiones de subprocesos durante la actualización de la imagen, pero aún así permitirá el aumento de rendimiento de cálculo concurrente que está buscando en sistemas de múltiples núcleos.