multithreadingJava

我试图在我的Java Mandelbrot应用程序中实现multithreading:

这是我到目前为止:

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

出现的问题是,在绘制图像时,图像中会显示不正确的像素:

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

当我检查线程时,例如:

 threads[i].isAlive(); 

图像似乎成功显示,但图像需要更长的时间(最多3倍)才能渲染。

我想知道两件事。

  1. 我哪里错了?

  2. 将Mandelbrots绘制到BufferedImage进行大量迭代(> 1000)的最佳方法是什么?

我希望他的是@Michael Chang所暗示的。 我调整了代码以在band中呈现。

请注意,我无法对此进行测试。 我不熟悉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); } } } 

绘图不是线程安全的,因此无法从多个线程绘制相同的{screen,image,whatever}。 这些行之间可能会中断您的线程(即可能发生上下文切换):

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

一种选择是为每个线程提供其自己的图像(例如,图像的四分之一作为图块)以进行绘制,然后在最后将图像绘制在一起。

绘图不是线程安全的 – 一个线程可以重绘另一个线程的结果。

您可以使用volatile关键字创建二维结果数组,表示结果像素。 线程可以保存到此数组而不会发生冲突,并且当数组填满(线程结束,您可以使用.join()方法)时,您可以立即绘制所有内容。

要解决问题,请更改

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

至:

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

这将防止在更新映像期间发生线程冲突,但仍允许您在多核系统上寻求并发计算性能提升。