Пример кода для imgscalr AsyncScalr
Может кто-нибудь поделиться примером кода imgscalr, используя AsyncScalr для изменения размера кода? Я пытаюсь использовать imgscalr (класс Scalr) для обработки изображений. Это хорошая и простая в использовании библиотека, однако, она выдает OutOfMemoryException слишком часто. Я надеюсь, что использование AsyncScalr решит мою проблему для низких нагрузок.
2 ответа
Если вы знакомы с библиотеками Java Concurrent, использование класса AsyncScalr действительно просто; если вы не знакомы с новыми параллельными библиотеками, суть в следующем:
- Вызовите метод API, который выполняет некоторую работу в неизвестной точке в будущем; вызов метода возвращает Future, который оборачивает фактическую работу.
- Исходный вызов API фактически ставит работу в очередь; если он не занят, он, скорее всего, выполнит работу сразу, но если он занят и очередь огромна, работа может занять некоторое время (в данном случае "работа" - это масштабирование образ).
- Вызывающий код (ваш код), который хочет получить результат, может продолжать работу до тех пор, пока Future.isDone() не вернет true, чтобы указать, что работа была завершена, ИЛИ вызывающий код может просто заблокироваться, пока операция не будет выполнена, вызывая: Future.get () - этот метод возвращает результат работы, в данном случае BufferedImage, который представляет масштабированный результат.
Код буквально выглядит так:
// Block until result is done
BufferedImage result = AsyncScalr.resize(origImage, 125).get();
Разница между этим кодом и использованием класса Scalr напрямую состоит в том, что в многопоточных системах, если вы вызываете Scalr.resize() (или любой из операций с изображениями) из ВСЕХ ваших потоков, каждый из этих потоков собирается запустите дорогостоящую операцию с изображениями, заполняя ваш процессор параллельной работой и замедляя работу системы (блокируя другие процессы, выполняющиеся на нем, такие как БД или веб-сервер).
Используя класс AsyncScalr, вы можете безопасно вызывать AsyncScalr.resize (или любые другие операции) из любого числа потоков и никогда не беспокоиться о том, чтобы загружать хост-систему работой; AsyncScalr.THREAD_COUNT определяет, сколько одновременных заданий может выполняться одновременно; Обычно вы хотите установить это число ядер на хост-машине или меньше количества ядер, если на хост-машине также размещены другие важные сервисы, такие как база данных или веб-сервер (чтобы вы не заглушали другие процессы, когда масштабирование становится занятым).
Вы можете установить это значение потока в командной строке во время запуска вашего приложения с помощью системного свойства 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()
чтобы получить результат (или заблокировать, пока он не будет готов).
Надеюсь, это поможет!
Вот полезный метод, который планирует изменение размера изображения. Приятно то, что он возвращает 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.