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();
}
}
我需要从服务器检索一张巨大的图片,但服务器无法执行此操作,因为图片太大。我可以给 "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();
}
}