同步错误 - ArrayList 被多个线程访问
Syncronization error - ArrayList accessed by multiple Threads
这是在 java。我的程序的目的是每秒多次截取计算机屏幕的屏幕截图,并找到所有具有特殊红色阴影的像素。然后,它找到所有红色像素的平均位置。
为了提高我的图像处理效率,我创建了3个线程,每个线程处理1/4的像素。这些线程加上原始线程将因此处理所有像素。但是,我的 avgLocation() 方法一直出错。这是一个空指针异常,我认为这是因为其他线程正在更改包含所有红色像素的列表的大小,这导致程序访问不存在的像素数据。为了解决这个问题,我在 Thread2 的代码之后对 Thread1 和 Thread2 使用了 .join,然后在 Thread3 的代码段之后使用了 .join。因此,在我调用 avgLocation 方法之前应该连接所有线程,但只要屏幕上出现特定的红色阴影,它仍然会不断给出 NullPointerException。这是堆栈跟踪
Exception in thread "main" java.lang.NullPointerException
at Images.avgLocation(Images.java:151)
at Images.processImage(Images.java:133)
at Images.main(Images.java:169)
第 151 行是
xSum += list.get(i)[0];
第 133 行是
这是我的代码:
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.ArrayList;
public class Images {
//takes in an input image and a target color and a bound to check within, returns Point to target
public static Point processImage(BufferedImage img, Color color, int error) throws InterruptedException {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//create final variables to use for thread
int redLowerF = redLower;
int redHigherF = redHigher;
int greenLowerF = greenLower;
int greenHigherF = greenHigher;
int blueLowerF = blueLower;
int blueHigherF = blueHigher;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
//stores locations
ArrayList <Integer[]> locations = new ArrayList <>();
for (int i = 0; i < pixels.length/4; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLower && r<=redHigher && g>=greenLower && g <= greenHigher && b >= blueLower && b <= blueHigher) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
Thread thread1 = new Thread( () -> {
for (int i = pixels.length/4; i < pixels.length/2; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread1.start();
Thread thread2 = new Thread( () -> {
for (int i = pixels.length/2; i < pixels.length*3/4; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread2.start();
thread1.join();
thread2.join();
Thread thread3 = new Thread( () -> {
for (int i = pixels.length*3/4; i < pixels.length; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread3.start();
thread3.join();
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations);
}
//given 2D array of locations, finds average location of set and returns as point
public static Point avgLocation (ArrayList<Integer[]> list) {
//if no points in list, return an impossible point on screen
if (list.size() == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100;
int avgY = 100;
int xSum = 0;
int ySum = 0;
//loop through array
for (int i = 0; i < list.size(); i++) {
//for each location, add up coordinates
xSum += list.get(i)[0];
ySum += list.get(i)[1];
}
avgX = xSum/list.size();
avgY = ySum/list.size();
return new Point (avgX, avgY);
}
public static void main (String[] args) {
Robot robot = null;
try {
robot = new Robot();
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
while (true) {
try {
processImage(robot.createScreenCapture(new Rectangle(0,0,d.width,d.height)), new Color (255, 0, 0), 10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
线程通过添加新元素并发地修改和更改locations
ArrayList
。 ArrayList
不适合这种没有同步的并发修改。
不过有CopyOnWriteArrayList可以支持不同步的并发修改
尝试替换行:
ArrayList<Integer[]> locations= new ArrayList<>();
用下面一行:
final List<Integer []> locations = new CopyOnWriteArrayList<>();
ArrayList 不是线程安全的
创建一个类似
的方法
static private void addToList (ArrayList<Integer[]> list, Integer [] p) {
synchronized (list) {
list.add(p);
}
}
可以在您的线程中调用,例如
Image.addToList(locations, point);
好吧,你的线程同步有什么问题是你有 none。您试图通过使用 join()
来避免并发访问 ArrayList
的尝试失败了,因为您仍然同时拥有 thread1
和 thread2
运行。如果您同时从 运行ning 停止它们,ArrayList
的任何问题都会消失,但当然,您也不会从使用多个线程中获得任何好处。
现在,您可以用线程安全实现替换列表或为访问添加手动同步,但这不是您应该这样做的方式。这种同步增加了一个开销,该开销可能大于并发计算的任何潜在收益。毕竟,每个像素执行的算法并不复杂。
相反,您最好让所有线程使用它们自己的列表,它们可以无竞争地添加到列表中。然后,在两个线程完成工作后合并列表。
为了简化此任务,您应该重构代码,使一个方法具有参数来控制要处理的范围,而不是四次使用相同的代码。
此外,您可以将 Integer[]
替换为 int[]
,以避免将原始值装箱到对象中。
// takes in an input image and a target color and a bound to check within,
// returns Point to target
public static Point processImage(BufferedImage img, Color color, int error)
throws InterruptedException, ExecutionException {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
//stores locations
ArrayList <int[]> locations = processPixels(pixels, height, 0, pixels.length, 1,
redLower, redHigher, greenLower, greenHigher, blueLower, blueHigher);
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations);
}
private static ArrayList<int[]> processPixels(
int[] pixels, int height, int from, int to, int divide,
int redLower, int redHigher, int greenLower, int greenHigher,
int blueLower, int blueHigher)
throws InterruptedException, ExecutionException {
List<FutureTask<ArrayList<int[]>>> jobs = new ArrayList<>();
while(divide > 0 && to - from > 1) {
int divideF = --divide;
int mid = (from + to) >>> 1;
int jobFrom = from, jobTo = mid;
FutureTask<ArrayList<int[]>> f = new FutureTask<>(() -> processPixels(
pixels, height, jobFrom, jobTo, divideF,
redLower, redHigher, greenLower, greenHigher, blueLower, blueHigher));
new Thread(f).start();
jobs.add(f);
from = mid;
}
ArrayList<int[]> locations = new ArrayList<>();
for (int i = from; i < to; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLower && r<=redHigher && g>=greenLower && g <= greenHigher
&& b >= blueLower && b <= blueHigher) {
int[] point = {i % height, i/height};
locations.add(point);
}
}
for(FutureTask<ArrayList<int[]>> j: jobs) locations.addAll(j.get());
return locations;
}
//given 2D array of locations, finds average location of set and returns as point
public static Point avgLocation(ArrayList<int[]> list) {
//if no points in list, return an impossible point on screen
if (list.size() == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100, avgY = 100;
int xSum = 0, ySum = 0;
//loop through array
for (int i = 0; i < list.size(); i++) {
//for each location, add up coordinates
xSum += list.get(i)[0];
ySum += list.get(i)[1];
}
avgX = xSum/list.size();
avgY = ySum/list.size();
return new Point(avgX, avgY);
}
processPixels
将完成处理指定范围的工作,但首先 divide
将范围减半并生成新线程以用一半调用相同的方法,同时保留另一半.由于其他线程执行的方法也会根据divide
创建新的线程,因此会有2divide
个线程在工作。因此,为单线程传递 0
,为两个线程传递 1
,为四个线程传递 2
,为八个线程传递 3
,依此类推。
所有线程都将执行它们的工作,添加到本地列表,然后添加子任务的结果(如果有的话)。
但请注意,类似的概念已在 Java 中实现,并且动态适应可用的 CPU 核心数量和实际工作负载。使用时免费获得
// takes in an input image and a target color and a bound to check within,
// returns Point to target
public static Point processImage(BufferedImage img, Color color, int error) {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//create final variables to use for function
int redLowerF = redLower;
int redHigherF = redHigher;
int greenLowerF = greenLower;
int greenHigherF = greenHigher;
int blueLowerF = blueLower;
int blueHigherF = blueHigher;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
List<int[]> locations = IntStream.range(0, pixels.length).parallel()
.filter(i -> {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
return r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF
&& b >= blueLowerF && b <= blueHigherF;
})
.mapToObj(i -> new int[] {i % height, i / height})
.collect(Collectors.toList());
//stores locations
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations);
}
//given 2D array of locations, finds average location of set and returns as point
public static Point avgLocation(List<int[]> list) {
//if no points in list, return an impossible point on screen
if (list.size() == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100, avgY = 100;
int xSum = 0, ySum = 0;
//loop through array
for (int i = 0; i < list.size(); i++) {
//for each location, add up coordinates
xSum += list.get(i)[0];
ySum += list.get(i)[1];
}
avgX = xSum/list.size();
avgY = ySum/list.size();
return new Point(avgX, avgY);
}
我们可以进一步改进代码,因为我们不需要将点表示为 int[]
数组,当我们知道索引和坐标之间的关系时:
public static Point processImage(BufferedImage img, Color color, int error) {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//create final variables to use for function
int redLowerF = redLower;
int redHigherF = redHigher;
int greenLowerF = greenLower;
int greenHigherF = greenHigher;
int blueLowerF = blueLower;
int blueHigherF = blueHigher;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
int[] locations = IntStream.range(0, pixels.length).parallel()
.filter(i -> {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
return r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF
&& b >= blueLowerF && b <= blueHigherF;
})
.toArray();
//stores locations
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations, height);
}
private static Point avgLocation(int[] locations, int height) {
if (locations.length == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100, avgY = 100;
int xSum = 0, ySum = 0;
//loop through array
for (int i: locations) {
int x = i % height, y = i / height;
//for each location, add up coordinates
xSum += x;
ySum += y;
}
avgX = xSum/locations.length;
avgY = ySum/locations.length;
return new Point(avgX, avgY);
}
这是在 java。我的程序的目的是每秒多次截取计算机屏幕的屏幕截图,并找到所有具有特殊红色阴影的像素。然后,它找到所有红色像素的平均位置。
为了提高我的图像处理效率,我创建了3个线程,每个线程处理1/4的像素。这些线程加上原始线程将因此处理所有像素。但是,我的 avgLocation() 方法一直出错。这是一个空指针异常,我认为这是因为其他线程正在更改包含所有红色像素的列表的大小,这导致程序访问不存在的像素数据。为了解决这个问题,我在 Thread2 的代码之后对 Thread1 和 Thread2 使用了 .join,然后在 Thread3 的代码段之后使用了 .join。因此,在我调用 avgLocation 方法之前应该连接所有线程,但只要屏幕上出现特定的红色阴影,它仍然会不断给出 NullPointerException。这是堆栈跟踪
Exception in thread "main" java.lang.NullPointerException
at Images.avgLocation(Images.java:151)
at Images.processImage(Images.java:133)
at Images.main(Images.java:169)
第 151 行是
xSum += list.get(i)[0];
第 133 行是 这是我的代码:
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.ArrayList;
public class Images {
//takes in an input image and a target color and a bound to check within, returns Point to target
public static Point processImage(BufferedImage img, Color color, int error) throws InterruptedException {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//create final variables to use for thread
int redLowerF = redLower;
int redHigherF = redHigher;
int greenLowerF = greenLower;
int greenHigherF = greenHigher;
int blueLowerF = blueLower;
int blueHigherF = blueHigher;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
//stores locations
ArrayList <Integer[]> locations = new ArrayList <>();
for (int i = 0; i < pixels.length/4; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLower && r<=redHigher && g>=greenLower && g <= greenHigher && b >= blueLower && b <= blueHigher) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
Thread thread1 = new Thread( () -> {
for (int i = pixels.length/4; i < pixels.length/2; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread1.start();
Thread thread2 = new Thread( () -> {
for (int i = pixels.length/2; i < pixels.length*3/4; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread2.start();
thread1.join();
thread2.join();
Thread thread3 = new Thread( () -> {
for (int i = pixels.length*3/4; i < pixels.length; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF && b >= blueLowerF && b <= blueHigherF) {
Integer[] point = {i % height, i/height};
locations.add(point);
}
}
});
thread3.start();
thread3.join();
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations);
}
//given 2D array of locations, finds average location of set and returns as point
public static Point avgLocation (ArrayList<Integer[]> list) {
//if no points in list, return an impossible point on screen
if (list.size() == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100;
int avgY = 100;
int xSum = 0;
int ySum = 0;
//loop through array
for (int i = 0; i < list.size(); i++) {
//for each location, add up coordinates
xSum += list.get(i)[0];
ySum += list.get(i)[1];
}
avgX = xSum/list.size();
avgY = ySum/list.size();
return new Point (avgX, avgY);
}
public static void main (String[] args) {
Robot robot = null;
try {
robot = new Robot();
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
while (true) {
try {
processImage(robot.createScreenCapture(new Rectangle(0,0,d.width,d.height)), new Color (255, 0, 0), 10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
线程通过添加新元素并发地修改和更改locations
ArrayList
。 ArrayList
不适合这种没有同步的并发修改。
不过有CopyOnWriteArrayList可以支持不同步的并发修改
尝试替换行:
ArrayList<Integer[]> locations= new ArrayList<>();
用下面一行:
final List<Integer []> locations = new CopyOnWriteArrayList<>();
ArrayList 不是线程安全的
创建一个类似
的方法 static private void addToList (ArrayList<Integer[]> list, Integer [] p) {
synchronized (list) {
list.add(p);
}
}
可以在您的线程中调用,例如
Image.addToList(locations, point);
好吧,你的线程同步有什么问题是你有 none。您试图通过使用 join()
来避免并发访问 ArrayList
的尝试失败了,因为您仍然同时拥有 thread1
和 thread2
运行。如果您同时从 运行ning 停止它们,ArrayList
的任何问题都会消失,但当然,您也不会从使用多个线程中获得任何好处。
现在,您可以用线程安全实现替换列表或为访问添加手动同步,但这不是您应该这样做的方式。这种同步增加了一个开销,该开销可能大于并发计算的任何潜在收益。毕竟,每个像素执行的算法并不复杂。
相反,您最好让所有线程使用它们自己的列表,它们可以无竞争地添加到列表中。然后,在两个线程完成工作后合并列表。
为了简化此任务,您应该重构代码,使一个方法具有参数来控制要处理的范围,而不是四次使用相同的代码。
此外,您可以将 Integer[]
替换为 int[]
,以避免将原始值装箱到对象中。
// takes in an input image and a target color and a bound to check within,
// returns Point to target
public static Point processImage(BufferedImage img, Color color, int error)
throws InterruptedException, ExecutionException {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
//stores locations
ArrayList <int[]> locations = processPixels(pixels, height, 0, pixels.length, 1,
redLower, redHigher, greenLower, greenHigher, blueLower, blueHigher);
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations);
}
private static ArrayList<int[]> processPixels(
int[] pixels, int height, int from, int to, int divide,
int redLower, int redHigher, int greenLower, int greenHigher,
int blueLower, int blueHigher)
throws InterruptedException, ExecutionException {
List<FutureTask<ArrayList<int[]>>> jobs = new ArrayList<>();
while(divide > 0 && to - from > 1) {
int divideF = --divide;
int mid = (from + to) >>> 1;
int jobFrom = from, jobTo = mid;
FutureTask<ArrayList<int[]>> f = new FutureTask<>(() -> processPixels(
pixels, height, jobFrom, jobTo, divideF,
redLower, redHigher, greenLower, greenHigher, blueLower, blueHigher));
new Thread(f).start();
jobs.add(f);
from = mid;
}
ArrayList<int[]> locations = new ArrayList<>();
for (int i = from; i < to; i++) {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
if (r >= redLower && r<=redHigher && g>=greenLower && g <= greenHigher
&& b >= blueLower && b <= blueHigher) {
int[] point = {i % height, i/height};
locations.add(point);
}
}
for(FutureTask<ArrayList<int[]>> j: jobs) locations.addAll(j.get());
return locations;
}
//given 2D array of locations, finds average location of set and returns as point
public static Point avgLocation(ArrayList<int[]> list) {
//if no points in list, return an impossible point on screen
if (list.size() == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100, avgY = 100;
int xSum = 0, ySum = 0;
//loop through array
for (int i = 0; i < list.size(); i++) {
//for each location, add up coordinates
xSum += list.get(i)[0];
ySum += list.get(i)[1];
}
avgX = xSum/list.size();
avgY = ySum/list.size();
return new Point(avgX, avgY);
}
processPixels
将完成处理指定范围的工作,但首先 divide
将范围减半并生成新线程以用一半调用相同的方法,同时保留另一半.由于其他线程执行的方法也会根据divide
创建新的线程,因此会有2divide
个线程在工作。因此,为单线程传递 0
,为两个线程传递 1
,为四个线程传递 2
,为八个线程传递 3
,依此类推。
所有线程都将执行它们的工作,添加到本地列表,然后添加子任务的结果(如果有的话)。
但请注意,类似的概念已在 Java 中实现,并且动态适应可用的 CPU 核心数量和实际工作负载。使用时免费获得
// takes in an input image and a target color and a bound to check within,
// returns Point to target
public static Point processImage(BufferedImage img, Color color, int error) {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//create final variables to use for function
int redLowerF = redLower;
int redHigherF = redHigher;
int greenLowerF = greenLower;
int greenHigherF = greenHigher;
int blueLowerF = blueLower;
int blueHigherF = blueHigher;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
List<int[]> locations = IntStream.range(0, pixels.length).parallel()
.filter(i -> {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
return r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF
&& b >= blueLowerF && b <= blueHigherF;
})
.mapToObj(i -> new int[] {i % height, i / height})
.collect(Collectors.toList());
//stores locations
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations);
}
//given 2D array of locations, finds average location of set and returns as point
public static Point avgLocation(List<int[]> list) {
//if no points in list, return an impossible point on screen
if (list.size() == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100, avgY = 100;
int xSum = 0, ySum = 0;
//loop through array
for (int i = 0; i < list.size(); i++) {
//for each location, add up coordinates
xSum += list.get(i)[0];
ySum += list.get(i)[1];
}
avgX = xSum/list.size();
avgY = ySum/list.size();
return new Point(avgX, avgY);
}
我们可以进一步改进代码,因为我们不需要将点表示为 int[]
数组,当我们知道索引和坐标之间的关系时:
public static Point processImage(BufferedImage img, Color color, int error) {
long start = System.nanoTime();
//bounds to check on color components, make sure all within [0,255]
int redLower = color.getRed() - error;
int redHigher = color.getRed() + error;
int greenLower = color.getGreen() - error;
int greenHigher = color.getGreen() + error;
int blueLower = color.getBlue() - error;
int blueHigher = color.getBlue() + error;
//place all components within [0,255]
if (redLower < 0) redLower = 0;
if (redHigher > 255) redHigher = 255;
if (greenLower < 0) greenLower = 0;
if (greenHigher > 255) greenHigher = 255;
if (blueLower < 0) blueLower = 0;
if (blueHigher > 255) blueHigher = 255;
//create final variables to use for function
int redLowerF = redLower;
int redHigherF = redHigher;
int greenLowerF = greenLower;
int greenHigherF = greenHigher;
int blueLowerF = blueLower;
int blueHigherF = blueHigher;
//data of image
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
int width = img.getWidth();
int height = img.getHeight();
int[] locations = IntStream.range(0, pixels.length).parallel()
.filter(i -> {
int p = pixels[i];
// get red
int r = (p >> 16) & 0xff;
// get green
int g = (p >> 8) & 0xff;
// get blue
int b = p & 0xff;
//check if all components within bounds
return r >= redLowerF && r<=redHigherF && g>=greenLowerF && g <= greenHigherF
&& b >= blueLowerF && b <= blueHigherF;
})
.toArray();
//stores locations
long end = System.nanoTime();
System.out.println((end-start)/1000000);
return avgLocation(locations, height);
}
private static Point avgLocation(int[] locations, int height) {
if (locations.length == 0) {
return new Point (-100, -100);
}
//coordinates of average location (set to 100 to reveal bug easily)
int avgX = 100, avgY = 100;
int xSum = 0, ySum = 0;
//loop through array
for (int i: locations) {
int x = i % height, y = i / height;
//for each location, add up coordinates
xSum += x;
ySum += y;
}
avgX = xSum/locations.length;
avgY = ySum/locations.length;
return new Point(avgX, avgY);
}