当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2023-10-17 09:38:52 443观看
导读如果您的应用程序与那些能够同时处理多个任务的应用程序相比表现不佳,很可能是因为它是单线程的。解决这个问题的方法之一是采用多线程技术。以下是一些可以考虑的方法:线程(Thread)并行流(Parallel Streams)ExecutorServic

如果您的应用程序与那些能够同时处理多个任务的应用程序相比表现不佳,很可能是因为它是单线程的。解决这个问题的方法之一是采用多线程技术。Cze28资讯网——每日最新资讯28at.com

以下是一些可以考虑的方法:Cze28资讯网——每日最新资讯28at.com

  • 线程(Thread)
  • 并行流(Parallel Streams)
  • ExecutorService
  • ForkJoinPool
  • CompletableFuture

适当地使用这些方法,可以彻底改变您的应用程序,并推动您的职业发展。下面我们来看看如何将您的应用程序转变为高效的多线程应用。Cze28资讯网——每日最新资讯28at.com

Cze28资讯网——每日最新资讯28at.com

1. 线程(Thread)

第一种选择是使用线程(Thread)类。通过这种方式,您可以直接控制线程的创建和管理。以下是一个示例:Cze28资讯网——每日最新资讯28at.com

CustomTask 每隔50毫秒从0数到 count - 1。Cze28资讯网——每日最新资讯28at.com

public class CustomTask implements Runnable {    private final String name;    private final int count;    CustomTask(String name, int count) {        this.name = name;        this.count = count;    }    @Override    public void run() {        for (int i = 0; i < count; i++) {            System.out.println(name + "-" + i + " from " +                    Thread.currentThread().getName());            try {                Thread.sleep(50);            } catch (InterruptedException e) {                throw new RuntimeException(e);            }        }    }}

a、b 和 c 是该类的三个实例。Cze28资讯网——每日最新资讯28at.com

Thread a = new Thread(new CustomTask("a", 5));Thread b = new Thread(new CustomTask("b", 10));Thread c = new Thread(new CustomTask("c", 5));

请注意,b 预计计数的次数是其他实例的两倍。您希望在 a 和 c 顺序运行的同时运行 b。Cze28资讯网——每日最新资讯28at.com

Cze28资讯网——每日最新资讯28at.com

您可以非常容易地实现这种行为。Cze28资讯网——每日最新资讯28at.com

// 首先启动 a 和 b。a.start();b.start();//  a 完成后开始 c。a.join();c.start();

以下是结果:Cze28资讯网——每日最新资讯28at.com

a-0 from Thread-0b-0 from Thread-1b-1 from Thread-1a-1 from Thread-0b-2 from Thread-1a-2 from Thread-0b-3 from Thread-1a-3 from Thread-0b-4 from Thread-1a-4 from Thread-0b-5 from Thread-1c-0 from Thread-2b-6 from Thread-1c-1 from Thread-2b-7 from Thread-1c-2 from Thread-2b-8 from Thread-1c-3 from Thread-2b-9 from Thread-1c-4 from Thread-2

a 和 b 同时开始运行,轮流输出。a 完成后,c 开始执行。此外,它们全部在不同的线程中运行。通过手动创建 Thread 实例,您可以完全控制它们。Cze28资讯网——每日最新资讯28at.com

然而,请注意,低级线程处理也需要同步和资源管理,这可能更容易出错和复杂。Cze28资讯网——每日最新资讯28at.com

2. 并行流(Parallel Streams)

当您需要对大型集合中的所有元素应用相同、重复且独立的任务时,并行流非常有效。Cze28资讯网——每日最新资讯28at.com

例如,图像调整大小是一个需要按顺序运行的繁重任务;当您有多个图像需要调整大小时,如果按顺序执行,将需要很长时间才能完成。在这种情况下,您可以使用并行流并行调整它们的大小,如下所示。Cze28资讯网——每日最新资讯28at.com

private static List<BufferedImage> resizeAll(List<BufferedImage> sourceImages,                                             int width, int height) {    return sourceImages            .parallelStream()            .map(source -> resize(source, width, height))            .toList();}

这样,图像将同时调整大小,节省了大量宝贵的时间。Cze28资讯网——每日最新资讯28at.com

3. ExecutorService

当实现不需要精确的线程控制时,可以考虑使用 ExecutorService。ExecutorService 提供了更高层次的线程管理抽象,包括线程池、任务调度和资源管理。Cze28资讯网——每日最新资讯28at.com

ExecutorService 是一个接口,它最常见的用法是线程池。假设您有大量的异步任务堆积在一起,但是同时运行所有任务——每个任务占用一个线程——似乎太多了。线程池可以通过限制最大线程数来帮助您。Cze28资讯网——每日最新资讯28at.com

下面的示例中,我们使用 Executors.newFixedThreadPool() 实例化 ExecutorService 来使用 3 个线程运行 10 个任务。每个任务只打印一行。请注意,我们在之前的部分中重用了之前定义的 CustomTask。Cze28资讯网——每日最新资讯28at.com

ExecutorService executorService = Executors.newFixedThreadPool(3);for (int i = 0; i < 10; i++) {    executorService.submit(new CustomTask(String.valueOf(i), 1));}executorService.shutdown();

这将打印以下结果:Cze28资讯网——每日最新资讯28at.com

0-0 from pool-1-thread-12-0 from pool-1-thread-31-0 from pool-1-thread-24-0 from pool-1-thread-33-0 from pool-1-thread-25-0 from pool-1-thread-16-0 from pool-1-thread-17-0 from pool-1-thread-38-0 from pool-1-thread-29-0 from pool-1-thread-3

10 个任务在 3 个线程中运行。通过限制特定任务使用的线程数,您可以根据优先级分配线程数:对于重要且频繁的任务使用更多线程,对于琐碎或偶尔的任务使用较少线程。ExecutorService 具有高效和简洁的特点,是大多数多线程场景的首选选项。Cze28资讯网——每日最新资讯28at.com

如果您需要更多的控制和灵活性,请查看 ThreadPoolExecutor,它是 Executors.newFixedThreadPool() 返回的 ExecutorService 的实际实现。您可以直接创建其实例或将返回的 ExecutorService 实例转换为 ThreadPoolExecutor 实例以获得更多控制权。Cze28资讯网——每日最新资讯28at.com

4. ForkJoinPool

ForkJoinPool是另一种线程池,正如其名称所示。虽然它在许多其他异步方法的底层使用中,但对于可以分解为较小且独立子任务的任务来说,它也非常强大,这些任务可以通过分而治之的策略来解决。Cze28资讯网——每日最新资讯28at.com

其中一个任务是图像调整大小。图像调整大小是分而治之问题的一个很好的例子。使用ForkJoinPool,您可以将图像分成两个或四个较小的图像,并同时调整它们的大小。以下是ImageResizeAction的示例,它将图像调整为给定的大小。Cze28资讯网——每日最新资讯28at.com

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;    }    @Override    protected 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用于定义递归的调整大小操作。在此示例中,图像被分成两半并并行调整大小。Cze28资讯网——每日最新资讯28at.com

您可以使用以下代码运行ImageResizeAction:Cze28资讯网——每日最新资讯28at.com

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的帮助,您现在能够更高效地调整图像的大小,具有更好的可伸缩性,并最大程度地利用资源。Cze28资讯网——每日最新资讯28at.com

5. CompletableFuture

通过CompletableFuture,您可以完全发挥Future的功能,并拥有许多额外的特性。其中最突出的功能是它能够链式地连接异步操作,使您能够构建复杂的异步管道。Cze28资讯网——每日最新资讯28at.com

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()打印生成的数据。结果如下所示:Cze28资讯网——每日最新资讯28at.com

ForkJoinPool.commonPool-worker-1ForkJoinPool.commonPool-worker-1ForkJoinPool.commonPool-worker-1Result: My name is Hyuni Kim

有3个任务没有在主线程中运行,这表明它们与主逻辑并行运行。当您有具有结果并需要链接的任务时,CompletableFuture将是一个很好的选择。Cze28资讯网——每日最新资讯28at.com

6. 总结

多线程是一种强大的工具,可以帮助开发人员优化性能、提升用户体验、增强并发处理能力,并充分利用计算机的资源。Cze28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-13639-0.html掌握这五种多线程方法,提高Java代码效率

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 实战!用 Arthas 定位 Spring Boot 接口的超时问题,让应用起飞

下一篇: 8000字+22张图探秘SpringCloud配置中心的核心原理

标签:
  • 热门焦点
  • 官方承诺:K60至尊版将会首批升级MIUI 15

    全新的MIUI 15今天也有了消息,在官宣了K60至尊版将会搭载天玑9200+处理器和独显芯片X7的同时,Redmi给出了官方承诺,K60至尊重大更新首批升级,会首批推送MIUI 15。也就是说虽然
  • 2023年Q2用户偏好榜:12+256G版本成新主流

    3月份的性能榜、性价比榜和好评榜之后,就要轮到2023年的第二季度偏好榜了,上半年的新机潮已经过去,最明显的肯定就是大内存和存储的机型了,另外部分中端机也取消了屏幕塑料支架
  • 如何正确使用:Has和:Nth-Last-Child

    我们可以用CSS检查,以了解一组元素的数量是否小于或等于一个数字。例如,一个拥有三个或更多子项的grid。你可能会想,为什么需要这样做呢?在某些情况下,一个组件或一个布局可能会
  • 得物效率前端微应用推进过程与思考

    一、背景效率工程随着业务的发展,组织规模的扩大,越来越多的企业开始意识到协作效率对于企业团队的重要性,甚至是决定其在某个行业竞争中突围的关键,是企业长久生存的根本。得物
  • 这款新兴工具平台,让你的电脑效率翻倍

    随着信息技术的发展,我们获取信息的渠道越来越多,但是处理信息的效率却成为一个瓶颈。于是各种工具应运而生,都在争相解决我们的工作效率问题。今天我要给大家介绍一款效率
  • Temu起诉SHEIN,跨境电商战事升级

    来源 | 伯虎财经(bohuFN)作者 | 陈平安日前据外媒报道,拼多多旗下跨境电商平台Temu正对竞争对手SHEIN提起新诉讼,诉状称Shein&ldquo;利用市场支配力量强迫服装厂商与之签订独家
  • 苹果、三星、惠普等暂停向印度出口笔记本和平板电脑

    集微网消息,据彭博社报道,在8月3日印度突然禁止在没有许可证的情况下向印度进口电脑/平板及显示器等产品后,苹果、三星电子和惠普等大公司暂停向印度
  • 7月4日见!iQOO 11S官宣:“鸡血版”骁龙8 Gen2+200W快充加持

    上半年已接近尾声,截至目前各大品牌旗下的顶级旗舰都已悉数亮相,而下半年即将推出的顶级旗舰已经成为了数码圈爆料的主流,其中就包括全新的iQOO 11S系
  • 滴滴违法违规被罚80.26亿 共存在16项违法事实

    滴滴违法违规被罚80.26亿 存在16项违法事实开始于2121年7月,历经一年时间,网络安全审查办公室对“滴滴出行”网络安全审查终于有了一个暂时的结束。据“网信
Top