I'm trying to paint a Srtm3-Tile with the Help of Multithreading. If i use only a single Thread every works fine, but when i use Multithreading the graphic ends up in a mess. Currently i use 12 Cores - any idea?
Here is the code:
Thread-Init this piece of code resides in the paint method:
int cpuCount = Runtime.getRuntime().availableProcessors(); //12 Cores
int blockSize = size / cpuCount;
BigMapPOJO bigMapPOJO = new BigMapPOJO(srtmBorderPOJO.getFileName(), bigMap);
SrtmV2Thread threadArray[] = new SrtmV2Thread[cpuCount];
//Init Threads, cpuCount=1 works fine...
for (int i = 0; i < cpuCount; i++) {
SrtmV2Thread srtmV2Thread;
if (i == cpuCount - 1) {
srtmV2Thread = new SrtmV2Thread(g, map, bigMapPOJO, 0 + (i * blockSize), size, srtmBorderPOJO, min, max);
} else {
srtmV2Thread = new SrtmV2Thread(g, map, bigMapPOJO, 0 + (i * blockSize), blockSize + (i * blockSize), srtmBorderPOJO, min, max);
}
threadArray[i] = srtmV2Thread;
}
for (int i = 0; i < threadArray.length; i++) {
//Start Threads
threadArray[i].start();
}
for (int i = 0; i < threadArray.length; i++) {
try {
//Wait for completion
threadArray[i].join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
Run Method of the Thread:
@Override
public void run() {
GeoPosition geoPosition0 = new GeoPosition(srtmV2BorderPOJO.getCoord0().getLat(), srtmV2BorderPOJO.getCoord0().getLon());
GeoPosition geoPosition1 = new GeoPosition(srtmV2BorderPOJO.getCoord1().getLat(), srtmV2BorderPOJO.getCoord1().getLon());
GeoPosition geoPosition2 = new GeoPosition(srtmV2BorderPOJO.getCoord2().getLat(), srtmV2BorderPOJO.getCoord2().getLon());
GeoPosition geoPosition3 = new GeoPosition(srtmV2BorderPOJO.getCoord3().getLat(), srtmV2BorderPOJO.getCoord3().getLon());
Point2D pt0 = map.getTileFactory().geoToPixel(geoPosition0, map.getZoom());
Point2D pt1 = map.getTileFactory().geoToPixel(geoPosition1, map.getZoom());
Point2D pt2 = map.getTileFactory().geoToPixel(geoPosition2, map.getZoom());
Point2D pt3 = map.getTileFactory().geoToPixel(geoPosition3, map.getZoom());
/*
0-1
3-2
*/
//calulate width and height of a single tile
int resHor = (int) ((pt1.getX() - pt0.getX()));
int resVer = (int) ((pt3.getY() - pt0.getY()));
int horStep = (int) (size / (double) resHor);
int verStep = (int) (size / (double) resVer);
//start and end comes from the thread init
for (int y = start; y < end; y += verStep) {
for (int x = 0; x < size; x += horStep) {
double ver = y / (double) (size);
double hor = x / (double) (size);
GeoPosition geoPosition = new GeoPosition(srtmV2BorderPOJO.getCoord0().getLat() - ver, srtmV2BorderPOJO.getCoord0().getLon() + hor);
Point2D ptHeight = map.getTileFactory().geoToPixel(geoPosition, map.getZoom());
short height = bigMapPOJO.getBigMap()[y][x];
if (height < 0) {
height = 0;
}
double percent = getPercentFromHeight(min, max, height);
Color colorHeight = genColor(percent);
double red = colorHeight.getRed();
double green = colorHeight.getGreen();
double blue = colorHeight.getBlue();
//draw point in calculated color, according to height
java.awt.Color color = new java.awt.Color((int) (red * 255), (int) (green * 255), (int) (blue * 255), 255);
g.setColor(color);
Point point = new Point((int) ptHeight.getX(), (int) ptHeight.getY());
g.draw(new Rectangle(point));
}
}
}
I see at least two reasons:
Graphics2D
is an abstract class which is not guaranteed to be thread safe. So executing whatever on it is a mistake in general. It could work in some cases, but doesn't have to.
Your code is not thread-tolerant itself: g.setColor(color);
is not atomic/synchronized with a g.draw(new Rectangle(point));
call. So it would overlap between threads and be executed in an unpredictable order across them like this:
thread 1: setColor(color1);
thread 2: setColor(color2);
thread 1: draw() // with color2;
thread 2: draw() // with color2 also;
So consider:
Synchronize setColor
with draw
for example in a basic way:
Point point = new Point((int) ptHeight.getX(), (int) ptHeight.getY());
synchronized(lockObject) {
g.setColor(color);
g.draw(new Rectangle(point));
}
where the lockObject
is an object shared between threads
Graphics2D
with multiple threads. Consider doing only your math and other preparations in multiple thread and call Graphics2D
from a single one.