Java:这些线程为什么/监视什么?

3

我有一个多线程Java应用程序,它将图像分成4个块,然后4个线程(我有一个四核CPU)分别处理图像的每个块,将其转换为灰度。

我发现它的速度相当慢,因此我使用了NetBeans分析器,并发现这些线程在相当长的时间内处于“监视”(等待)状态。例如,

this

(绿色=运行,红色=监视)

我尝试使用不同数量的线程,例如 2,发现仍然会出现这种情况(唯一不会出现此问题的是使用1个线程时)。

在线程内部,我注释了它们代码的一些部分,直到我将“大延迟”缩小到以下语句:

newImage.setRGB(i,j,newColor.getRGB()); // Write the new value for that pixel

如果将此行注释掉,代码运行速度会快得多(几乎快5倍),而且没有线程监控: enter image description here 那么为什么这一行会引起如此大的延迟呢?是Color库(与BufferedImage一起)吗?现在我要尝试获取一个int数组作为RGB值,而不是使用Color对象,看看效果如何。
以下是源代码:
PixelsManipulation.java(主类):
public final class PixelsManipulation{

private static Sequential sequentialGrayscaler = new Sequential();  

public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException {  

    File file = new File("src/pixelsmanipulation/hiresimage.jpg");
    FileInputStream fis = new FileInputStream(file);  
    BufferedImage image = ImageIO.read(fis); //reading the image file  

    int rows = 2; // 2 rows and 2 cols will split the image into quarters
    int cols = 2;  
    int chunks = rows * cols; // 4 chunks, one for each quarter of the image  
    int chunkWidth = image.getWidth() / cols; // determines the chunk width and height  
    int chunkHeight = image.getHeight() / rows;  
    int count = 0;  
    BufferedImage imgs[] = new BufferedImage[chunks]; // Array to hold image chunks  

    for (int x = 0; x < rows; x++) {  
        for (int y = 0; y < cols; y++) {  
            //Initialize the image array with image chunks  
            imgs[count] = new BufferedImage(chunkWidth, chunkHeight, image.getType());  
            // draws the image chunk  

            Graphics2D gr = imgs[count++].createGraphics(); // Actually create an image for us to use
            gr.drawImage(image, 0, 0, chunkWidth, chunkHeight, chunkWidth * y, chunkHeight * x, chunkWidth * y + chunkWidth, chunkHeight * x + chunkHeight, null);  
            gr.dispose();

        }  
    } 

    //writing mini images into image files  
    for (int i = 0; i < imgs.length; i++) {  
        ImageIO.write(imgs[i], "jpg", new File("img" + i + ".jpg"));  
    }  
    System.out.println("Mini images created");  

    // Start threads with their respective quarters (chunks) of the image to work on
    // I have a quad-core machine, so I can only use 4 threads on my CPU
    Parallel parallelGrayscaler = new Parallel("thread-1", imgs[0]);
    Parallel parallelGrayscaler2 = new Parallel("thread-2", imgs[1]);
    Parallel parallelGrayscaler3 = new Parallel("thread-3", imgs[2]);
    Parallel parallelGrayscaler4 = new Parallel("thread-4", imgs[3]);

    // Sequential:
    long startTime = System.currentTimeMillis();

    sequentialGrayscaler.ConvertToGrayscale(image);

    long stopTime = System.currentTimeMillis();
    long elapsedTime = stopTime - startTime;
    System.out.println("Sequential code executed in " + elapsedTime + " ms.");

    // Multithreaded (parallel):
    startTime = System.currentTimeMillis();

    parallelGrayscaler.start();
    parallelGrayscaler2.start();
    parallelGrayscaler3.start();
    parallelGrayscaler4.start();

    // Main waits for threads to finish so that the program doesn't "end" (i.e. stop measuring time) before the threads finish
    parallelGrayscaler.join();
    parallelGrayscaler2.join();
    parallelGrayscaler3.join();
    parallelGrayscaler4.join();

    stopTime = System.currentTimeMillis();
    elapsedTime = stopTime - startTime;
    System.out.println("Multithreaded (parallel) code executed in " + elapsedTime + " ms.");
}
}

Parallel.java:

// Let each of the 4 threads work on a different quarter of the image
public class Parallel extends Thread{//implements Runnable{

private String threadName;
private static BufferedImage myImage; // Calling it "my" image because each thread will have its own unique quarter of the image to work on
private static int width, height; // Image params

Parallel(String name, BufferedImage image){
    threadName = name;
    System.out.println("Creating "+ threadName);
    myImage = image;
    width = myImage.getWidth();
    height = myImage.getHeight();

}

public void run(){
    System.out.println("Running " + threadName);

    // Pixel by pixel (for our quarter of the image)
    for (int j = 0; j < height; j++){
        for (int i = 0; i < width; i++){

            // Traversing the image and converting the RGB values (doing the same thing as the sequential code but on a smaller scale)
            Color c = new Color(myImage.getRGB(i,j));

            int red = (int)(c.getRed() * 0.299);
            int green = (int)(c.getGreen() * 0.587);
            int blue  = (int)(c.getBlue() * 0.114);

            Color newColor = new Color(red + green + blue, red + green + blue, red + green + blue);

            myImage.setRGB(i,j,newColor.getRGB()); // Write the new value for that pixel


        }
    }

    File output = new File("src/pixelsmanipulation/"+threadName+"grayscale.jpg"); // Put it in a "lower level" folder so we can see it in the project view
    try {
        ImageIO.write(newImage, "jpg", output);
    } catch (IOException ex) {
        Logger.getLogger(Parallel.class.getName()).log(Level.SEVERE, null, ex);
    }
    System.out.println("Thread " + threadName + " exiting. ---");
}
}

我是一个Java多线程(以及使用BufferedImage)的初学者,只是好奇为什么它很慢。


显然有些东西缺失了,因为我没有看到 newImage 被创建或声明。 - gpasch
setRgb 是同步的,如果我没记错的话。 - Voo
@gpasch 那是我在复制粘贴之前本打算改的一个地方,应该是myImage。对于给你造成的困惑,我表示抱歉。 - Touchdown
1个回答

4

为什么Parallel.myImage是静态的?这会导致所有线程共享同一张图片。这可能解释了它们彼此等待的原因。


哇,我完全忽略了那一点。你是对的,这就解释了为什么它们都在等待彼此完成。 - Touchdown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接