简体   繁体   中英

Thread safety with ExecutorService and CountDownLatch

I have cycle, where i download image, I need to load for example 10 images and merge them in one image. In my interest what images will all loaded. This is how i do that. I have executor for limit thread count, and i have CountDownLatch barrier which waiting until all images will be loaded.

CountDownLatch barrier = new CountDownLatch(images.size());
private static ExecutorService executorService = Executors.newFixedThreadPool(MAX_THREAD_POOL);

for (Image image : images) {
    executorService.execute(new ImageRunnable(image, barrier));
}
    barrier.await();

In ImageRunnable i download image like this. From google static map.

String url ="my url"
try {
    URL target = new URL(url);
    ImageIO.read(target);
    barrier.countDown();
    //exaggerated logic
} catch (IOException e) {
    System.out.println("Can not load image, " + e);
}

Other people said to me that i can get case when all threads in executor will be busy and my algorithm never ends because he will wait until all threads get barrier.await() point (deadlock). How said to me it's will happen when ImageIO.read(target) called and connection will established but HTTP session never be closed (response from server do not come back). This can happen? I thought in this case i get some exception and bad thread will interrupted. Exactly that happens when I start my cycle but on third image i close internet connection by firewall. On output I get broken image like network was closed and image not loaded to end. Am I wrong?

The concern is you may throw an exception and never count down your latch.

I would consider doing this:

String url ="my url"
try {
    URL target = new URL(url);
    ImageIO.read(target);
} catch (IOException e) {
    System.out.println("Can not load image, " + e);
    throw e;
} finally {
    barrier.countDown();
}

Throw the exception to let the world know you've run into a problem and may not be able to complete (you know you can't recover from it) but at the very least let the barrier get lowered. I'd rather have to deal with an exception than a deadlock.

Just to flesh out my comment:

CompletionService<Image> service = new ExecutorCompletionService<Image>(
  Executors.newFixedThreadPool(nThreads));
for (Image image : images) {
  service.submit(new ImageRunnable(image), image);
}
try {
  for (int i = 0; i < images.size(); i++) {
    service.take();
  }
} catch (InterruptedException e) {
  // someone wants this thread to cancel peacefully; either exit the thread
  // or at a bare minimum do this to pass the interruption up
  Thread.currentThread().interrupt();
}

There. That's it.

If you're concerned about enforcing timeouts on the HTTP connection, my quick and dirty research suggests something like...

URL target = // whatever;
URLConnection connection = target.openConnection();
connection.setReadTimeout(timeoutInMilliseconds);
InputStream stream;
try {
  stream = connection.getInputStream();
  return ImageIO.read(stream);
} finally {
  if (stream != null) { stream.close(); } 
}

Apart from moving barrier.countDown() to finally block as suggested by @corsiKa , make sure your code ever finishes. Set some timeout on reading URL and on await() :

barrier.await(1, TimeUnit.MINUTES);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM