3

有人可以分享使用 AsyncScalr 调整代码大小的 imgscalr 示例代码吗?我正在尝试使用 imgscalr(Scalr 类)进行图像处理。它是一个很好且易于使用的库,但是,它经常给出 OutOfMemoryException。我希望使用 AsyncScalr 可以解决我的低负载问题。

4

2 回答 2

3

如果您熟悉 Java 并发库,那么使用 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 类之间的区别在于,在多线程系统中,如果您从所有线程调用 Scalr.resize() (或任何图像操作),那么这些线程中的每一个都将开始一个昂贵的图像操作,并发工作使您的 CPU 泛滥并减慢系统速度(阻塞在其上运行的其他进程,例如 DB 或 Web 服务器)。

使用AsyncScalr 类,您可以安全地从任意数量的线程调用 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()以获取结果(或阻塞直到它准备好)。

希望有帮助!

于 2012-03-19T14:14:11.443 回答
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 :-(");
  }
});

ListenableFuture并且FutureCallback都在Guava 库中定义。

于 2015-06-29T09:47:26.583 回答