掌握这五种多线程方法,提高Java代码效率( 二 )

10 个任务在 3 个线程中运行 。通过限制特定任务使用的线程数,您可以根据优先级分配线程数:对于重要且频繁的任务使用更多线程,对于琐碎或偶尔的任务使用较少线程 。ExecutorService 具有高效和简洁的特点,是大多数多线程场景的首选选项 。
如果您需要更多的控制和灵活性,请查看 ThreadPoolExecutor,它是 Executors.newFixedThreadPool() 返回的 ExecutorService 的实际实现 。您可以直接创建其实例或将返回的 ExecutorService 实例转换为 ThreadPoolExecutor 实例以获得更多控制权 。
4. ForkJoinPoolForkJoinPool是另一种线程池,正如其名称所示 。虽然它在许多其他异步方法的底层使用中,但对于可以分解为较小且独立子任务的任务来说,它也非常强大,这些任务可以通过分而治之的策略来解决 。
其中一个任务是图像调整大小 。图像调整大小是分而治之问题的一个很好的例子 。使用ForkJoinPool , 您可以将图像分成两个或四个较小的图像,并同时调整它们的大小 。以下是ImageResizeAction的示例,它将图像调整为给定的大小 。
package multithreading;import JAVA.awt.image.BufferedImage;import java.util.concurrent.RecursiveAction;public class ImageResizeAction extends RecursiveAction {private static final int THRESHOLD = 100;private final BufferedImage sourceImage;private final BufferedImage targetImage;private final int startRow;private final int endRow;private final int targetWidth;private final int targetHeight;public ImageResizeAction(BufferedImage sourceImage,BufferedImage targetImage,int startRow, int endRow,int targetWidth, int targetHeight) {this.sourceImage = sourceImage;this.targetImage = targetImage;this.startRow = startRow;this.endRow = endRow;this.targetWidth = targetWidth;this.targetHeight = targetHeight;}@Overrideprotected void compute() {if (endRow - startRow <= THRESHOLD) {resizeImage();} else {int midRow = startRow + (endRow - startRow) / 2;invokeAll(new ImageResizeAction(sourceImage, targetImage,startRow, midRow, targetWidth, targetHeight),new ImageResizeAction(sourceImage, targetImage,midRow, endRow, targetWidth, targetHeight));}}private void resizeImage() {int sourceWidth = sourceImage.getWidth();double xScale = (double) targetWidth / sourceWidth;double yScale = (double) targetHeight / sourceImage.getHeight();for (int y = startRow; y < endRow; y++) {for (int x = 0; x < sourceWidth; x++) {int targetX = (int) (x * xScale);int targetY = (int) (y * yScale);int rgb = sourceImage.getRGB(x, y);targetImage.setRGB(targetX, targetY, rgb);}}}}请注意,ImageResizeAction继承了RecursiveAction 。RecursiveAction用于定义递归的调整大小操作 。在此示例中,图像被分成两半并并行调整大小 。
您可以使用以下代码运行ImageResizeAction:
public static void mAIn(String[] args) throws IOException {String sourceImagePath = "source_image.jpg";String targetImagePath = "target_image.png";int targetWidth = 300;int targetHeight = 100;BufferedImage sourceImage = ImageIO.read(new File(sourceImagePath));BufferedImage targetImage = new BufferedImage(targetWidth, targetHeight,BufferedImage.TYPE_INT_RGB);ForkJoinPool forkJoinPool = new ForkJoinPool();forkJoinPool.invoke(new ImageResizeAction(sourceImage, targetImage,0, sourceImage.getHeight(), targetWidth, targetHeight));ImageIO.write(targetImage, "png", new File(targetImagePath));System.out.println("图像调整大小成功!");}借助ForkJoinPool的帮助,您现在能够更高效地调整图像的大小 , 具有更好的可伸缩性,并最大程度地利用资源 。
5. CompletableFuture通过CompletableFuture,您可以完全发挥Future的功能 , 并拥有许多额外的特性 。其中最突出的功能是它能够链式地连接异步操作,使您能够构建复杂的异步管道 。
public static void main(String[] args) {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName());return "Hyuni Kim";}).thenApply((data) -> {System.out.println(Thread.currentThread().getName());return "我的名字是" + data;}).thenAccept((data) -> {System.out.println(Thread.currentThread().getName());System.out.println("结果:" + data);});future.join();}上述代码展示了CompletableFuture的一个关键方面:链式操作 。通过CompletableFuture.supplyAsync() , 首先创建并运行一个返回字符串结果的CompletableFuture 。thenApply()接受前一个任务的结果,并执行其他操作,本例中是添加一个字符串 。最后,thenAccept()打印生成的数据 。结果如下所示:
ForkJoinPool.commonPool-worker-1ForkJoinPool.commonPool-worker-1ForkJoinPool.commonPool-worker-1Result: My name is Hyuni Kim


推荐阅读