views:

158

answers:

4

I have a collection of BufferedImage instances, one main image and some subimages created by calling getSubImage on the main image. The subimages do not overlap. I am also making modifications to the subimage and I want to split this into multiple threads, one per subimage.

From my understanding of how BufferedImage, Raster and DataBuffer work, this should be safe because:

  • Each instance of BufferedImage (and its respective WritableRaster and SampleModel) is accessed from only one thread.
  • The shared ColorModel is immutable
  • The DataBuffer has no fields that can be modified (the only thing that can change is elements of the backing array.)
  • Modifying disjoint segments of an array in separate threads is safe.

However I cannot find anything in the documentation that says that it is definitely safe to do this. Can I assume it is safe? I know that it is possible to work on copies of the child Rasters but I would prefer to avoid this because of memory constraints.

Otherwise, is it possible to make the operation thread-safe without copying regions of the parent image?

A: 

I haven't found any clear evidence of thread safety of BufferedImage, but you probably can solve your problem the next way:

Instead of simultaneous processing of subimages by different workers, try to process many images in the way that every worker consumes different subimages of the same image. The same worker will be processing subimages of the same image, but sequentially.

Your workers would be busy until there are less images than workers remained.

Revert this problem:

        W1       W2      W3
Img1 |-------|-------|-------|
        W1       W2      W3
Img2 |-------|-------|-------|
To:

        W1       W1      W1
Img1 |-------|-------|-------|
        W2       W2      W2
Img2 |-------|-------|-------|
    
leopoldkot
Then there is no advantage to splitting the image.
finnw
I would prefer to continue processing subimages of a single image in multiple threads to keep the latency down. Even if it turns out that it requires copying the subimages.
finnw
+4  A: 

Have you looked into using JAI to manage your 'subimages' as tiles? It seems like a better use of resources if you don't have to hang onto the original image BufferedImage instance as well as all its subImage BufferedImage instance. Information about JAI can be found here: JAI README

There is a class, TiledImage, which implements the RenderedImage interface (giving it a common ancestor with BufferedImage). According to the JAI documentation:

The use of tiling also facilitates the use of multiple threads for computation. Previously allocated tiles may also be re-used to save memory.

Using one of these implementations of RenderedImage is often preferrable to a BufferedImage anyway, since the BufferedImage maintains an image snapshot in memory for the entire image. JAI uses a rendering chain and can recycle tiles as needed to fit memory constraints.

Jeff
+2  A: 

That's a good analysis, and it sounds correct to me. There is no shared data, so concurrent access should be fine. However, you need some kind of guarantee to be sure, more than a educated guess that it should work. Even if you find a statement saying "BufferedImage is designed to be used concurrently" - there is no guarantee that this is the case in practice.

To be as sure as you can be, you can write a concurrent unit test using ConTest. The concurrent test harness instruments your code and injects artificially induced context switches to expose concurrency bugs. This will test the BufferedImage code and your own code, so you can have a high degree of confidence that it is thread safe.

mdma
A: 

If none of these answers satisfy (enough) you could do something that will conclusively settle the question at a heavy(?) price.

Examine the source for BufferedImage, DataBuffer, Raster, etc. It's the only way.

Allen
I have already done that, but being safe on one implementation does not necessarily mean it will be safe on another.
finnw