5

I have a web application where users upload images. We validate the image data via ImageIO.read() and perform a few simple transformations on the resulting BufferedImage before saving it to disk.

While performing load testing, we realized that when many requests come in at the same time, they are being blocked in the ImageIO.read() call. Digging deeper, we noticed that the JPEGImageReader is synchronized and that only one BufferedImage is being created at a time.

Has anyone else come across this? I have been Googling this for a few days now and haven't come across another person that has had this issue, so maybe I am doing something wrong. I cannot come up with any logical reason why this would be. It seems to do with not being able to create individual Readers and Writers per image for some memory leak issue, but that explanation seems rather thin to me.

EDIT: Here is a performance tool that breaks down what is taking so long. I believe this is due to all of the threads waiting for the synchronization lock, JPEGImageReader source.

EDIT: The JAI libraries would have worked except that OpenJDK has removed support for critical parts of it, explicitly the JPEG codec.

SOLUTION: Given the amount of time I spent trying to find an alternative solution and failing to do so, my best solution was to process the images asynchronously, with respect to the requests. So, when a request comes in, the raw image data is stored as a supposedly valid image; then, an asynchronous process outside of the request threads will process each image one at a time. Due to the synchronicity of the ImageIO library, there is no gain from trying to do multiple at once. The images could be processed in parallel given that the library is not synchronous, only inefficient.

While doing the processing asynchronously adds a level of complexity, it's probably a good idea, with respect to modifying the image. What doesn't work is that we cannot process the original image in each request, which means that our system must make the assumption that each image is valid image data. When the asynchronous processor does get around to processing an image, inconsistencies in the system may occur if the data is bad.

user1236874
  • 167
  • 3
  • 13
  • 1
    Are you running multiple threads? Are you saying multiple `JPEGImageReader` instances are serializing on the class (i.e. single-threading)? How have you determined that this is the case? If so, where is the monitor on which they are all locking/waiting? – Jim Garrison Jul 29 '13 at 20:36
  • It's definitely multi-threaded since they are individual requests in Tomcat. I am starting to believe that ImageIO is only creating one underlying JPEGImageReader under the hood. See: [JPEGImageReader](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/com/sun/imageio/plugins/jpeg/JPEGImageReader.java). We are 99% sure this is what is happening because, when we comment out the part that creates a BufferedImage and does the transformation and just save the InputStream contents to disk, the CPUs are actually being taxed. Otherwise, they sit at about 5% usage at load. – user1236874 Jul 29 '13 at 21:34
  • 1
    It sounds like you need to go down one layer in ImageIO and use the method that create readers, not the convenience methods. – user207421 Jul 29 '13 at 22:28
  • The problem with using the readers/writers directly is that I actually need some of the convenience utility from ImageIO. I want to be able to throw an input stream at it and get back a "validated" image. If I go lower, I will either be locked into one format, e.g. JPEG, or I will end up rewriting a good portion of the library. – user1236874 Jul 30 '13 at 13:49
  • From your profiling data (which may be incomplete), it seems the issue is a serious performance problem in OpenJDKs color management (LCMS). This CMS is not used in Sun/Oracle VMs I think. Have you tried other VMs? The fact that JPEGImageReader internally uses synchronization per instance should not lead you to the conclusion that ImageIO can't process multiple images at the same time. I'm pretty sure it can. – Harald K Aug 01 '13 at 20:23

2 Answers2

4

EDIT: Firstly, haraldK's answer is correct and my original answer was wrong. It cannot be deleted however, only edited the OP should reassign the tick.

The ThreadLock mechanism is there to stop threads accessing different readers from their own, not to stop threads from accessing their own readers independently of and simultaniously with other threads.

Therefore ImageIO's JPEGImageReader should work fine asynchronously.

pstanton
  • 35,033
  • 24
  • 126
  • 168
  • ps i know this isn't a complete "solution", sorry about that – pstanton Jul 29 '13 at 22:31
  • That's fine. If I get it working, I will give you the check and solved checkmark and update my issue with a solution. Thanks! – user1236874 Jul 30 '13 at 13:32
  • I am marking this as correct because it is, as best I can tell, the most optimal approach. The problem lies in OpenJDK, which no longer has support for JAI, but all other libraries that I have found rely on ImageIO. – user1236874 Aug 01 '13 at 14:39
  • @pstanton If you look carefully at the source code you posted, you'll see that the method does not block if used from multiple threads. It throws an `IllegalStateException`. PS: I do think you deserve credit for your effort in helping the OP find a workaround, don't get me wrong here. It's just that I think the conclusion is wrong, and trying to avoid confusion. :-) – Harald K Aug 06 '13 at 08:01
2

While performing load testing, we realized that when many requests come in at the same time, they are being blocked in the ImageIO.read() call. Digging deeper, we noticed that the JPEGImageReader is synchronized and that only one BufferedImage is being created at a time.

Has anyone else come across this?

As I mentioned in the comment section: From the performance analysis, it looks like a performance issue in the color management system of OpenJDK (lcms), because the color conversion should not take this long. My guess, without being able to debug further, is that this issue makes the decoding (appear) synchronized, even if ImageIO is capable of parallel decoding.

Here's an SSCCE that shows ImageIO perfectly capable of decoding multiple images at once.

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.stream.ImageInputStream;
import java.io.File;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

class MultipleJPEGDecoding {
    private static int threads = Runtime.getRuntime().availableProcessors();
    private static ExecutorService executorService = Executors.newFixedThreadPool(threads * 4);

    public static void main(final String[] args) throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            final int index = i;

            executorService.submit(new Runnable() {
                public void run() {
                    try {
                        ImageInputStream stream = ImageIO.createImageInputStream(new File(args[index % args.length]));
                        try {
                            Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
                            if (!readers.hasNext()) {
                                System.err.println("No reader!");
                                return;
                            }

                            ImageReader reader = readers.next();
                            reader.setInput(stream);
                            reader.addIIOReadProgressListener(new ProgressListener(index));

                            try {
                                reader.read(0);
                            }
                            finally {
                                reader.dispose();
                            }
                        }
                        finally {
                            stream.close();
                        }
                    }
                    catch (Exception e) {
                        System.err.printf("Error reading %d\n", index);
                        e.printStackTrace();
                    }
                }
            });
        }

        executorService.shutdown();
    }

    static class ProgressListener implements IIOReadProgressListener {
        final static AtomicInteger simultaneous = new AtomicInteger(0);

        final int index;
        int nextProgress = 25;

        public ProgressListener(int index) {
            this.index = index;
        }

        public void imageStarted(ImageReader source, int imageIndex) {
            int inProgress = simultaneous.incrementAndGet();
            System.err.printf("Started reading image %d (now decoding %d images simultaneous)...\n", index, inProgress);
        }

        public void imageComplete(ImageReader source) {
            int inProgress = simultaneous.decrementAndGet();
            System.err.printf("Done reading image %d%s.\n", index, inProgress > 0 ? String.format(" (still decoding %d other images)", inProgress) : "");
        }

        public void imageProgress(ImageReader source, float percentageDone) {
            if (percentageDone > nextProgress) {
                int inProgress = simultaneous.get();
                System.err.printf("Progress on image %d (now decoding %d images simultaneous)...\n", index, inProgress);
                nextProgress += 25;
            }
        }

        public void sequenceStarted(ImageReader source, int minIndex) {
        }

        public void sequenceComplete(ImageReader source) {
        }

        public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) {
        }

        public void thumbnailProgress(ImageReader source, float percentageDone) {
        }

        public void thumbnailComplete(ImageReader source) {
        }

        public void readAborted(ImageReader source) {
        }
    }
}

Output looks like:

Started reading image 5 (now decoding 2 images simultaneous)...
Started reading image 0 (now decoding 16 images simultaneous)...
Started reading image 14 (now decoding 15 images simultaneous)...
Started reading image 4 (now decoding 14 images simultaneous)...
Started reading image 13 (now decoding 1 images simultaneous)...
Started reading image 11 (now decoding 13 images simultaneous)...
Started reading image 2 (now decoding 12 images simultaneous)...
Started reading image 15 (now decoding 4 images simultaneous)...
Started reading image 8 (now decoding 11 images simultaneous)...
Started reading image 7 (now decoding 10 images simultaneous)...
Started reading image 9 (now decoding 9 images simultaneous)...
Started reading image 6 (now decoding 8 images simultaneous)...
Started reading image 10 (now decoding 7 images simultaneous)...
Started reading image 1 (now decoding 6 images simultaneous)...
Started reading image 3 (now decoding 5 images simultaneous)...
Started reading image 12 (now decoding 3 images simultaneous)...
Progress on image 11 (now decoding 16 images simultaneous)...
Progress on image 15 (now decoding 16 images simultaneous)...
Progress on image 13 (now decoding 16 images simultaneous)...
Progress on image 13 (now decoding 16 images simultaneous)...
Progress on image 9 (now decoding 16 images simultaneous)...
Progress on image 9 (now decoding 16 images simultaneous)...
Progress on image 1 (now decoding 16 images simultaneous)...
Progress on image 3 (now decoding 16 images simultaneous)...
Progress on image 1 (now decoding 16 images simultaneous)...
Progress on image 3 (now decoding 16 images simultaneous)...
Progress on image 1 (now decoding 16 images simultaneous)...
Progress on image 13 (now decoding 16 images simultaneous)...
Progress on image 5 (now decoding 16 images simultaneous)...
Progress on image 9 (now decoding 16 images simultaneous)...
Progress on image 3 (now decoding 16 images simultaneous)...
Done reading image 3 (still decoding 15 other images).
Started reading image 16 (now decoding 15 images simultaneous)...
Progress on image 11 (now decoding 15 images simultaneous)...
Done reading image 13 (still decoding 14 other images).
Started reading image 17 (now decoding 15 images simultaneous)...
Progress on image 5 (now decoding 15 images simultaneous)...
Progress on image 4 (now decoding 15 images simultaneous)...
Progress on image 11 (now decoding 15 images simultaneous)...
Done reading image 9 (still decoding 14 other images).
Progress on image 5 (now decoding 15 images simultaneous)...
Progress on image 17 (now decoding 15 images simultaneous)...
Done reading image 11 (still decoding 14 other images).
Started reading image 19 (now decoding 15 images simultaneous)...
Progress on image 17 (now decoding 15 images simultaneous)...
Done reading image 5 (still decoding 14 other images).
Started reading image 18 (now decoding 15 images simultaneous)...
Progress on image 15 (now decoding 15 images simultaneous)...
Done reading image 1 (still decoding 14 other images).
Started reading image 21 (now decoding 15 images simultaneous)...
Progress on image 15 (now decoding 15 images simultaneous)...
Progress on image 21 (now decoding 15 images simultaneous)...
Done reading image 15 (still decoding 14 other images).
Progress on image 6 (now decoding 14 images simultaneous)...
Progress on image 21 (now decoding 14 images simultaneous)...
Progress on image 7 (now decoding 14 images simultaneous)...
Progress on image 21 (now decoding 14 images simultaneous)...
Progress on image 7 (now decoding 14 images simultaneous)...
Progress on image 19 (now decoding 14 images simultaneous)...
Progress on image 19 (now decoding 14 images simultaneous)...
Progress on image 7 (now decoding 14 images simultaneous)...
Progress on image 19 (now decoding 14 images simultaneous)...
Done reading image 7 (still decoding 13 other images).
Started reading image 23 (now decoding 14 images simultaneous)...
Progress on image 10 (now decoding 14 images simultaneous)...
Progress on image 23 (now decoding 14 images simultaneous)...
Progress on image 14 (now decoding 14 images simultaneous)...
Started reading image 22 (now decoding 15 images simultaneous)...
Progress on image 0 (now decoding 15 images simultaneous)...
Done reading image 21 (still decoding 14 other images).
Started reading image 24 (now decoding 15 images simultaneous)...
Started reading image 20 (now decoding 16 images simultaneous)...
Progress on image 2 (now decoding 16 images simultaneous)...
Progress on image 8 (now decoding 16 images simultaneous)...
Progress on image 17 (now decoding 15 images simultaneous)...
Done reading image 17 (still decoding 14 other images).
Started reading image 25 (now decoding 15 images simultaneous)...
Progress on image 23 (now decoding 15 images simultaneous)...
Done reading image 19 (still decoding 15 other images).
Started reading image 26 (now decoding 16 images simultaneous)...
Progress on image 23 (now decoding 16 images simultaneous)...
Done reading image 23 (still decoding 15 other images).
Started reading image 27 (now decoding 16 images simultaneous)...
Progress on image 4 (now decoding 16 images simultaneous)...
Progress on image 27 (now decoding 16 images simultaneous)...
Progress on image 27 (now decoding 16 images simultaneous)...
Progress on image 6 (now decoding 16 images simultaneous)...
Progress on image 12 (now decoding 16 images simultaneous)...
Progress on image 20 (now decoding 16 images simultaneous)...
Progress on image 0 (now decoding 16 images simultaneous)...
Progress on image 25 (now decoding 16 images simultaneous)...
Progress on image 25 (now decoding 16 images simultaneous)...
Progress on image 25 (now decoding 16 images simultaneous)...
Progress on image 14 (now decoding 16 images simultaneous)...
Progress on image 10 (now decoding 16 images simultaneous)...
Progress on image 8 (now decoding 16 images simultaneous)...
Progress on image 18 (now decoding 16 images simultaneous)...
Done reading image 25 (still decoding 15 other images).

[...]

Progress on image 75 (now decoding 12 images simultaneous)...
Started reading image 73 (now decoding 13 images simultaneous)...
Progress on image 75 (now decoding 13 images simultaneous)...
Progress on image 73 (now decoding 13 images simultaneous)...
Progress on image 75 (now decoding 13 images simultaneous)...
Started reading image 74 (now decoding 14 images simultaneous)...
Progress on image 66 (now decoding 14 images simultaneous)...
Progress on image 64 (now decoding 14 images simultaneous)...
Progress on image 73 (now decoding 14 images simultaneous)...
Progress on image 60 (now decoding 14 images simultaneous)...
Progress on image 74 (now decoding 14 images simultaneous)...
Progress on image 58 (now decoding 14 images simultaneous)...
Done reading image 75 (still decoding 13 other images).
Progress on image 73 (now decoding 13 images simultaneous)...
Started reading image 77 (now decoding 14 images simultaneous)...
Done reading image 73 (still decoding 13 other images).
Progress on image 77 (now decoding 13 images simultaneous)...
Started reading image 78 (now decoding 14 images simultaneous)...
Progress on image 60 (now decoding 14 images simultaneous)...
Done reading image 48 (still decoding 13 other images).
Progress on image 77 (now decoding 13 images simultaneous)...
Started reading image 79 (now decoding 14 images simultaneous)...
Started reading image 70 (now decoding 15 images simultaneous)...
Progress on image 52 (now decoding 15 images simultaneous)...
Progress on image 71 (now decoding 15 images simultaneous)...
Started reading image 72 (now decoding 16 images simultaneous)...
Progress on image 71 (now decoding 16 images simultaneous)...
Progress on image 54 (now decoding 16 images simultaneous)...
Progress on image 68 (now decoding 16 images simultaneous)...
Progress on image 64 (now decoding 16 images simultaneous)...
Progress on image 66 (now decoding 16 images simultaneous)...
Progress on image 62 (now decoding 16 images simultaneous)...
Progress on image 79 (now decoding 16 images simultaneous)...
Progress on image 79 (now decoding 16 images simultaneous)...
Progress on image 79 (now decoding 16 images simultaneous)...
Progress on image 77 (now decoding 16 images simultaneous)...
Progress on image 68 (now decoding 16 images simultaneous)...
Done reading image 79 (still decoding 15 other images).
Done reading image 77 (still decoding 14 other images).
Started reading image 81 (now decoding 15 images simultaneous)...
Progress on image 74 (now decoding 15 images simultaneous)...
Progress on image 81 (now decoding 15 images simultaneous)...
Progress on image 81 (now decoding 15 images simultaneous)...
Progress on image 78 (now decoding 15 images simultaneous)...
Done reading image 60 (still decoding 14 other images).
Started reading image 82 (now decoding 15 images simultaneous)...
Started reading image 80 (now decoding 16 images simultaneous)...
Progress on image 76 (now decoding 14 images simultaneous)...
Progress on image 66 (now decoding 14 images simultaneous)...
Progress on image 70 (now decoding 14 images simultaneous)...
Done reading image 52 (still decoding 14 other images).
Done reading image 71 (still decoding 15 other images).
Progress on image 81 (now decoding 16 images simultaneous)...
Started reading image 84 (now decoding 15 images simultaneous)...
Started reading image 83 (now decoding 16 images simultaneous)...
Progress on image 58 (now decoding 16 images simultaneous)...
Progress on image 83 (now decoding 16 images simultaneous)...
Progress on image 83 (now decoding 16 images simultaneous)...
Progress on image 83 (now decoding 16 images simultaneous)...
Done reading image 81 (still decoding 15 other images).
Started reading image 85 (now decoding 16 images simultaneous)...
Progress on image 85 (now decoding 16 images simultaneous)...
Progress on image 74 (now decoding 16 images simultaneous)...
Done reading image 66 (still decoding 15 other images).
Started reading image 86 (now decoding 16 images simultaneous)...
Progress on image 64 (now decoding 16 images simultaneous)...
Progress on image 70 (now decoding 16 images simultaneous)...
Progress on image 78 (now decoding 16 images simultaneous)...
Progress on image 54 (now decoding 16 images simultaneous)...
Done reading image 58 (still decoding 15 other images).
Started reading image 87 (now decoding 16 images simultaneous)...
Progress on image 87 (now decoding 16 images simultaneous)...
Progress on image 84 (now decoding 16 images simultaneous)...
Progress on image 87 (now decoding 16 images simultaneous)...
Done reading image 64 (still decoding 15 other images).
Started reading image 88 (now decoding 16 images simultaneous)...
Progress on image 76 (now decoding 16 images simultaneous)...
Done reading image 83 (still decoding 15 other images).
Progress on image 62 (now decoding 15 images simultaneous)...
Progress on image 70 (now decoding 16 images simultaneous)...
Progress on image 85 (now decoding 16 images simultaneous)...
Started reading image 89 (now decoding 16 images simultaneous)...
Progress on image 72 (now decoding 16 images simultaneous)...
Progress on image 85 (now decoding 16 images simultaneous)...
Progress on image 89 (now decoding 16 images simultaneous)...
Done reading image 85 (still decoding 15 other images).
Progress on image 89 (now decoding 15 images simultaneous)...
Progress on image 82 (now decoding 15 images simultaneous)...
Progress on image 80 (now decoding 15 images simultaneous)...
Done reading image 74 (still decoding 14 other images).
Started reading image 91 (now decoding 15 images simultaneous)...
Started reading image 90 (now decoding 16 images simultaneous)...
Done reading image 62 (still decoding 15 other images).
Progress on image 87 (now decoding 15 images simultaneous)...
Progress on image 68 (now decoding 15 images simultaneous)...
Done reading image 87 (still decoding 14 other images).
Progress on image 91 (now decoding 14 images simultaneous)...
Started reading image 93 (now decoding 15 images simultaneous)...
Progress on image 93 (now decoding 15 images simultaneous)...
Progress on image 91 (now decoding 15 images simultaneous)...
Progress on image 91 (now decoding 15 images simultaneous)...
Progress on image 80 (now decoding 15 images simultaneous)...
Started reading image 92 (now decoding 16 images simultaneous)...
Done reading image 91 (still decoding 15 other images).
Started reading image 94 (now decoding 16 images simultaneous)...
Progress on image 93 (now decoding 16 images simultaneous)...
Progress on image 72 (now decoding 16 images simultaneous)...
Progress on image 93 (now decoding 16 images simultaneous)...
Done reading image 93 (still decoding 15 other images).
Started reading image 95 (now decoding 16 images simultaneous)...
Progress on image 95 (now decoding 16 images simultaneous)...
Progress on image 95 (now decoding 16 images simultaneous)...
Progress on image 76 (now decoding 16 images simultaneous)...
Progress on image 95 (now decoding 16 images simultaneous)...
Progress on image 72 (now decoding 16 images simultaneous)...
Done reading image 95 (still decoding 15 other images).
Started reading image 96 (now decoding 16 images simultaneous)...
Progress on image 94 (now decoding 16 images simultaneous)...
Progress on image 89 (now decoding 15 images simultaneous)...
Done reading image 89 (still decoding 14 other images).
Done reading image 54 (still decoding 15 other images).
Started reading image 97 (now decoding 14 images simultaneous)...
Started reading image 98 (now decoding 15 images simultaneous)...
Done reading image 70 (still decoding 13 other images).
Started reading image 99 (now decoding 16 images simultaneous)...
Progress on image 82 (now decoding 16 images simultaneous)...
Progress on image 99 (now decoding 16 images simultaneous)...
Progress on image 99 (now decoding 16 images simultaneous)...
Progress on image 97 (now decoding 16 images simultaneous)...
Progress on image 97 (now decoding 16 images simultaneous)...
Progress on image 97 (now decoding 16 images simultaneous)...
Done reading image 68 (still decoding 15 other images).
Done reading image 97 (still decoding 14 other images).
Progress on image 78 (now decoding 14 images simultaneous)...
Progress on image 99 (now decoding 14 images simultaneous)...
Done reading image 99 (still decoding 13 other images).
Progress on image 86 (now decoding 13 images simultaneous)...
Done reading image 72 (still decoding 12 other images).
Progress on image 82 (now decoding 12 images simultaneous)...
Progress on image 98 (now decoding 12 images simultaneous)...
Progress on image 84 (now decoding 12 images simultaneous)...
Progress on image 90 (now decoding 12 images simultaneous)...
Done reading image 76 (still decoding 11 other images).
Progress on image 92 (now decoding 11 images simultaneous)...
Progress on image 80 (now decoding 11 images simultaneous)...
Progress on image 94 (now decoding 11 images simultaneous)...
Progress on image 88 (now decoding 11 images simultaneous)...
Progress on image 84 (now decoding 11 images simultaneous)...
Progress on image 90 (now decoding 11 images simultaneous)...
Progress on image 92 (now decoding 11 images simultaneous)...
Progress on image 86 (now decoding 11 images simultaneous)...
Progress on image 94 (now decoding 10 images simultaneous)...
Done reading image 84 (still decoding 9 other images).
Progress on image 92 (now decoding 9 images simultaneous)...
Done reading image 78 (still decoding 10 other images).
Progress on image 88 (now decoding 9 images simultaneous)...
Progress on image 90 (now decoding 9 images simultaneous)...
Done reading image 80 (still decoding 8 other images).
Done reading image 82 (still decoding 7 other images).
Progress on image 86 (now decoding 7 images simultaneous)...
Progress on image 96 (now decoding 7 images simultaneous)...
Progress on image 88 (now decoding 7 images simultaneous)...
Done reading image 90 (still decoding 6 other images).
Done reading image 92 (still decoding 5 other images).
Progress on image 98 (now decoding 5 images simultaneous)...
Done reading image 94 (still decoding 4 other images).
Done reading image 86 (still decoding 3 other images).
Progress on image 96 (now decoding 3 images simultaneous)...
Done reading image 88 (still decoding 2 other images).
Progress on image 98 (now decoding 2 images simultaneous)...
Progress on image 96 (now decoding 2 images simultaneous)...
Done reading image 98 (still decoding 1 other images).
Done reading image 96.
Community
  • 1
  • 1
Harald K
  • 26,314
  • 7
  • 65
  • 111
  • that doesn't prove anything. all you have proved is that you can queue up requests to the decoder. you have not proved that they are decoded simultaneously. the source code of the class clearly shows this would be impossible. – pstanton Aug 06 '13 at 04:42
  • @pstanton The source code says the *instance* is synchronized (but the API doc already specifies that readers are not to be shared by threads anyway). ImageIO will use different instances for multiple invocations, so it does decode multiple at images at a time (as my code shows). The requests are not queued, they are simultaneously decoded. I'll update the code to make it clearer. – Harald K Aug 06 '13 at 07:46
  • i admit i haven't tested this, but logging calls to your code is never going to show what is really happening. the only way to test this is to breakpoint the internals of `JPEGImageReader`. i don't want to seem like i'm defending my post, i'm not, i hope you are right. breakpoint the first hit to `JPEGImageReader.readInternal` and see if it stops all of the other 'simultanious' reads... – pstanton Aug 06 '13 at 10:00
  • @pstanton I don't understand what you think is wrong with my code? It now reports progress on multiple images simultaneous (I even wrote [a version that shows the images while decoding](https://dl.dropboxusercontent.com/u/9961638/MultipleJPEGDecoding.java), the code is a little long for SO, but please test it if you like). If I create a breakpoint, wouldn't that just suspend all my threads at that breakpoint? What would that prove? – Harald K Aug 06 '13 at 10:40
  • 1
    you are dead right. the 'threadLock' is to stop different threads accessing different readers, not to stop different threads having their own readers. apologies for confusing things. going to delete my answer ;) – pstanton Aug 06 '13 at 13:51
  • Good catch. I went through so many of these docs, that I thought it said that "due to a memory leak" or something, there should only ever be one instance of this class. That, combined with all of those synchronized methods, would have meant it would have been locked for sure. However, you are correct that the opposite must be true. I feel like, because the performance hit we experience was so great, there must be some synchronicity going on, but maybe the LCMS stuff is just really inefficient. Either way, thanks for the answer! – user1236874 Aug 07 '13 at 13:53
  • @user1236874 Could also be the LCMS implementation being improperly synchronized (which would be a very bad idea for a CMS) or similar. But it's hard to say without further analysis... You're probably happy with your async solution. :-) – Harald K Aug 07 '13 at 14:11