2012-03-17 96 views
3

有人可以使用AsyncScalr來共享imgscalr的示例代碼來調整代碼大小嗎?我正在嘗試使用imgscalr(Scalr類)進行圖像處理。這是一個很好用的庫,但是,經常會出現OutOfMemoryException。我希望使用AsyncScalr將解決我的低負載問題。imgscalr的示例代碼AsyncScalr

回答

3

如果您熟悉Java Concurrent庫,使用AsyncScalr類非常簡單;如果您不熟悉新的併發庫,那麼其要點如下:

  1. 調用將在未來某個未知點工作的API方法;該方法調用返回一個包裝實際工作的Future
  2. 最初的API調用實際上在內部排隊工作;如果不忙,它可能會馬上完成工作,但如果它很忙,隊列很大,可能需要一段時間才能完成工作(在這種情況下,「工作」是縮放圖片)。
  3. 想要結果的調用代碼(您的代碼)可以繼續工作,直到Future.isDone()返回true以指示工作已完成或者調用代碼可以阻塞,直到通過調用完成操作:Future.get() - 此方法返回在這種情況下,工作的結果是一個表示縮放結果的BufferedImage。

代碼字面上最終看起來像這樣:

// Block until result is done 
BufferedImage result = AsyncScalr.resize(origImage, 125).get(); 

此代碼並使用所述Scalr class之間的差異直接的是,在多線程系統中,如果調用Scalr.resize()(或任何圖像操作),所有線程中的每一個線程都將啓動昂貴的映像操作,使您的CPU充斥併發工作,並減慢系統爬行速度(扼殺其上運行的其他進程,如數據庫或網絡服務器)。

使用AsyncScalr class您可以安全地從任意數量的線程調用AsyncScalr.resize(或任何其他操作),並且永遠不用擔心主機系統氾濫; AsyncScalr.THREAD_COUNT確定一次可以同時發生多少個作業;您通常希望將其設置爲主機上的核心數量或少於核心數量(如果主機也承載數據庫或Web服務器等其他重要服務)(以確保不會阻塞其他重要服務)當縮放變得忙時進程)。

您可以在啓動時在命令行中爲您的應用程序使用「imgscalr.async.threadCount」系統屬性設置此線程值;默認情況下它是「2」,但如果您擔心繫統內存太低,可以將其設置爲「1」。

或者,如果你有工作,你的線程,而你等待結果能做到的,你可以做這樣的事情,真正利用好異步編程:

// Queue up the scaling operation (or any other op) 
Future<BufferedImage> result = AsyncScalr.resize(origImage, 125); 

/* 
* You can do other work here that doesn't need 'result', like making 
* DB calls, cleaning up temp files or anything else you might need to 
* do. 
*/ 

// Now we are all done and need the resulting image, so we wait for it. 
BufferedImage scaledImage = result.get(); 

// Do something with the image... 

如果你有中一個顯著量其他您可以在等待圖像縮放時做的工作,您可以簡單地循環result.isDone()並繼續工作,直到完成縮放操作;但是如果你只有一個離散的/特定數量的工作要做,不需要在isDone上循環,只需要執行該工作,然後調用Future.get()即可獲得結果(或阻塞直到準備就緒)。

希望有幫助!

+0

感謝@Riyad,AsyncScalr resize()方法對我來說工作正常。但是有一個小問題。用於獲取BufferedImage(AsyncScalr.resize方法的第一個參數)的ImageIO.read()方法正在成爲瓶頸。它將圖像文件加載到內存中,如果有多個線程這樣做,則會產生OutOfMemory異常。 imgscalr中是否有任何解決方案,或者我應該嘗試讓我的課程成爲Singleton? – 2012-03-22 15:05:43

+0

@DEREKN噢好吧,是的,先將很多圖像加載到內存中,然後再縮放它們會衝擊內存。使用單例是一個好主意;由於其所有的靜態方法,imgscalr很適合那種「util class」設計。你只需要一個爲圖像提供一個文件名的方法,這樣你就可以一次加載一個,縮放它,然後在加載下一個圖像之前處理圖像。 – 2012-03-23 15:25:26

+0

謝謝@Riyad,我在加載下一個圖像之前處理之前的圖像。它會進入應用程序的性能,但我認爲這是目前我能做的最好的場景。 – 2012-03-27 07:32:49

0

下面是一個實用方法,用於調度圖像的大小調整。關於這一點的好處是它會返回一個ListenableFuture,允許您附加一個圖像調整大小後執行的回調。

/** 
* Schedules the asynchronous resizing of an image. 
* <p> 
* Uses all available processors to do so. 
* 
* @param pathToImage 
*   the path to the image we want to resize 
* @param quality 
*   the quality we want the output image to have. One of {@link Method}. 
* @param desiredSize 
*   the resulting image will not have a bigger height or width than this 
* @return 
*   a {@link ListenableFuture} of the resulting image. You can add a callback to it using {@link Futures#addCallback(ListenableFuture, FutureCallback)} 
* @throws IOException 
*    if the image at {@code pathToImage} couldn't be read 
*/ 
public static ListenableFuture<BufferedImage> resize(String pathToImage, Method quality, int desiredSize) throws IOException { 

    // Configure AsyncScalr to use all available processors for resizing the images 
    String nrOfProcessors = String.valueOf(Runtime.getRuntime().availableProcessors()); 
    System.setProperty(AsyncScalr.THREAD_COUNT_PROPERTY_NAME, nrOfProcessors); 

    BufferedImage image = ImageIO.read(new File(pathToImage)); 
    Future<BufferedImage> ordinaryFuture = AsyncScalr.resize(image, quality, desiredSize); 
    ListenableFuture<BufferedImage> futureImage = JdkFutureAdapters.listenInPoolThread(ordinaryFuture); 
    image.flush(); 
    return futureImage; 
} 

這是你如何使用resize

ListenableFuture<BufferedImage> futureImage = resize("/path/to/img.png", Method.SPEED, 250); 

Futures.addCallback(futureImage, new FutureCallback<BufferedImage>() { 
    @Override 
    public void onSuccess(BufferedImage result) { 
      System.out.println("Your resized image is ready :-)"); 
    } 
    @Override 
    public void onFailure(Throwable t) { 
      System.out.println("Couldn't resize image :-("); 
    } 
}); 

ListenableFutureFutureCallback都在Guava library兩個定義。