2016-02-05 2 views
3

У меня многопоточное приложение Java, которое разбивает изображение на 4 куска, а затем 4 потока (у меня есть четырехъядерный процессор), каждый из которых работает с отдельным фрагментом изображения, преобразуя его в оттенки серого.Java: Почему/что такое мониторинг потоков?

Я обнаружил, что по какой-то причине это было довольно медленно, поэтому я использовал профилировщик NetBeans и обнаружил, что потоки «контролируют» (ждут) довольно много. Например,

this

(зеленый = бег, красный = мониторинг)

Я экспериментировал с различным количеством нитей, например, 2, и обнаружил, что это все еще произошло (единственный раз, когда этого не случилось, было 1 поток).

Внутри нитей, я заметил биты своего кода, пока я не сузил «большую задержку» вниз к этому утверждению:

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

Если закомментировать код работает НАМНОГО (почти 5x) быстрее и никакой контроля резьбы:

enter image description here

так почему же, что одна линии причина так много задержки? Это библиотека цветов (наряду с BufferedImage)? Прямо сейчас я собираюсь попытаться получить массив ints как значения 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), просто любопытно, почему это так медленно.

+0

Очевидно, что что-то не хватает, потому что я не вижу newImage где-либо созданного/объявленного – gpasch

+0

setRgb синхронизирован iirc – Voo

+0

@gpasch, что я хотел изменить перед копированием, это должно быть myImage. Извините за путаницу. – Touchdown

ответ

4

Почему именно Parallel.myImage статический? Это приведет к тому, что все потоки будут иметь одно и то же изображение. Это может объяснить, почему они ждут друг друга.

+0

Ничего себе, я полностью забыл об этом. Вы правы, это объясняет, почему они все ждут друг друга, чтобы закончить. – Touchdown

Смежные вопросы