java 中的一些并发设计

Some concurrency-design in java

我需要从服务器检索一张巨大的图片,但服务器无法执行此操作,因为图片太大。我可以给 "coordinates" 以便我可以检索该图片的一小部分。所以我将图片分成 100 个图块,然后将 10 个图块附加到一行,然后再附加每一行。当我按顺序执行时,效果很好。现在我下载 10 个图块 -> 将它们附加到一行 -> 下载接下来的 10 个图块 -> 将它们附加到一行 -> 将第二行附加到第一行 -> 下载接下来的 10 个图块等(简化):

public static void downloadWholeImage(){
   int xcoord=0;
   int ycoord=0;
   //outer loop for each row
   for(int i = 0; i<10; i++){
      //all tiles of a row are stored here
      BufferedImage[] tilesForRow = new BufferedImage[10];
      //inner loop for each tile of a row
      for(int j = 0; j<10; j++){
         //downloads the image
         BufferedImage tile = downloadImage(xcoord,ycoord);
         //removes all black pixels of the image
         BufferedImage[j] = removeBlackColor(tile);
         //increments xcoord so the next tile
         xcoord++;
      }
      //each row gets appended on top of the first row
      if(i==0){
         BufferedImage firstRow = appendTilesToRow(tilesForRow)
      } else{
         BufferedImage actualRow = appendTilesToRow(tilesForRow)
      }

      firstRow = appendActualToFirst(firstRow, actualRow); 
      //incrementing ycoord for next tile
      ycoord++;
   }
   writeImage(path,firstRow);
}

但是由于行非常大,所以需要很长时间才能将它们相互附加。在附加它们时,我认为我可以创建一个下载其他图块的线程。这就是问题所在。我不习惯并发编程。我知道它在技术上是如何完成的(实现 Runnable 等)但我应该如何设计它?我在另一个线程中有 运行 downloadImage(xcoord, ycoord) 的想法,但这导致了 removeBlackColor(tile) 放在哪里的问题。也在线程中或线程完成后?..谁应该等待什么(加入)?我希望这不是那么令人困惑。如果您在某处需要更多说明,请告诉我。

您似乎想要异步下载其他磁贴,而您的磁贴正在追加。

因此,您的答案似乎可以在这里找到: How does one implement a truly asynchronous java thread

注意:您无需等待。就让一个进程异步完成,而另一个是运行.

编辑:您也可以使用同步块来定义,在每次成功下载后应该执行特定的附加。

其中一种可能性是创建主线程,该主线程将 运行 N 个下载线程。每个下载线程都会从服务器获取图块,然后它可以将大图片的片段与图块交换。至于交换,它可能可以由许多线程同时完成,只要每个线程都在大画面的自己的部分上运行,这样它们就不会 "collide"。想象一个数组,其中一个线程使用索引 0 到 X,另一个线程使用索引 X+1 到数组的末尾。为了让它工作,你必须事先知道大图片的大小并对其进行初始化。但我想你已经这样做了。主线程只会等待所有小线程结束,这意味着工作已完成。

您可以这样做:为每个下载操作创建一个新线程,其中下载发生在 运行() 方法中。然后每个线程使用 join() 等待下一次下载完成并附加磁贴。

可以使用类似的方法来下载和追加每一行。

public class Download extends Thread {
    public static void main(String[] args) throws InterruptedException {
        Download[] download = new Download[10];
        for (int i = 0; i < 10; i++) {
            download[i] = (new Download(i));
            download[i].start();
        }
        for(int i = 0; i < 10; i++) {
            (download[i]).join();
            append(i);
        }
    }
    int val;
    Download(int val) {
        this.val = val;
    }
    @Override
    public void run() {
            System.out.println("Downloading tile " + val);
    }

编辑

如果您更喜欢实现 Runnable,语法如下

public class Download implements Runnable {
...
        Thread[] download = new Thread[10];
        for (int i = 0; i < 10; i++) {
            download[i] = new Thread(new Download(i));

这与第一个示例的工作方式完全相同,因此选择扩展 Thread 还是实现 Runnable 是个人喜好问题。

在我看来,您仍然需要按顺序下载行,并在每一行中确保在移动到下一行之前下载所有 10 个图块。

看看 java.util.concurrent 包,特别是 CountDownLatch

这是一个代码片段。 注意:它可能编译不正确,但给出了一个想法。

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

    CountDownLatch countLatch = new CountDownLatch(10);
    ExecutorService threadPool = Executors.newFixedThreadPool(10);
    ArrayList<SampleImageDownload> list = new ArrayList<SampleImageDownload>();
    int row =1;

    while (row <=10) {
        int tileno = 1;
        while(tileno <=10) {
         SampleImageDownload sample = new SampleImageDownload(countLatch, tileno);
         list.add(sample);
         threadPool.submit(sample); 
         tileno++;

        } 
            row++;
            countLatch.await(); // wait for all 10 tiles to download.
        //apendline
    }




}


class SampleImageDownload implements Runnable {

    int tileno;
    private CountDownLatch countLatch = null;
    BufferedImage tile = null;


    public SampleImageDownload(CountDownLatch countLatch, int tileno) {
        super();
        this.countLatch = countLatch;
        this.tileno = tileno;
    }


    @Override
    public void run() {

        // download and removeBlacktile
        // tile is ready
        countLatch.countDown();

    }

}