关于后端:Java并行流一次搞定多线程编程难题让你的程序飞起来

34次阅读

共计 4923 个字符,预计需要花费 13 分钟才能阅读完成。

前言

  在日常的工作中,为了进步程序的处理速度,充分利用多核处理器的性能,咱们须要手动编写多线程代码。然而多线程编程非常复杂,容易呈现死锁、竞态条件等问题,给咱们带来了很大的困扰。而 Java 并行流则提供了一种更加简略、易用、平安的并发编程形式,能够让咱们更加轻松地编写高效的并发程序。

应用多线程下载文件

public class MultiThreadExample {public static void main(String[] args) throws InterruptedException {

        List<String> urls = Arrays.asList(
            "https://example.com/file1.txt",
            "https://example.com/file2.txt",
            "https://example.com/file3.txt",
            "https://example.com/file4.txt",
            "https://example.com/file5.txt"
        );
        
        int threads = 5;
        int chunkSize = urls.size() / threads;
        int startIndex = 0;
        int endIndex = chunkSize;

        // 创立线程列表
        List<DownloadThread> downloadThreads = new ArrayList<>();

        // 启动多个线程进行文件下载
        for (int i = 0; i < threads; i++) {downloadThreads.add(new DownloadThread(urls, startIndex, endIndex));
            downloadThreads.get(i).start();
            startIndex += chunkSize;
            endIndex += chunkSize;
        }

        // 期待所有线程完结并汇总后果
        for (DownloadThread downloadThread : downloadThreads) {downloadThread.join();
        }

        System.out.println("文件下载实现");
    }
}

class DownloadThread extends Thread {
    private List<String> urls;
    private int start;
    private int end;

    public DownloadThread(List<String> urls, int start, int end) {
        this.urls = urls;
        this.start = start;
        this.end = end;
    }

    @Override
    public void run() {for (int i = start; i < end; i++) {HttpUtil.download(urls.get(i));
        }
    }
}

  咱们首先将要下载的文件 URL 存储在一个 List 中,而后为每个块创立了一个 DownloadThread 对象,并启动了多个线程进行下载操作。每个线程只负责解决 URL 的一个块,调用 HttpUtil.download 办法进行文件下载操作。最初,咱们期待所有线程完结即可。

应用 Fork/Join 进行下载

  import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class ForkJoinExample {public static void main(String[] args) {
        List<String> urls = Arrays.asList(
            "https://example.com/file1.txt", 
            "https://example.com/file2.txt", 
            "https://example.com/file3.txt", 
            "https://example.com/file4.txt", 
            "https://example.com/file5.txt"
        );
        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(new DownloadAction(urls, 0, urls.size()));
        System.out.println("文件下载实现");
    }

    static class DownloadAction extends RecursiveAction {
        private List<String> urls;
        private int start;
        private int end;

        public DownloadAction(List<String> urls, int start, int end) {
            this.urls = urls;
            this.start = start;
            this.end = end;
        }

        @Override
        protected void compute() {if (end - start <= 1) {HttpUtil.download(urls.get(start));
                return;
            }

            int mid = (start + end) / 2;
            DownloadAction leftAction = new DownloadAction(urls, start, mid);
            DownloadAction rightAction = new DownloadAction(urls, mid, end);
            invokeAll(leftAction, rightAction);
        }
    }
}

  在这个示例中,咱们应用了 ForkJoin 框架来实现文件下载。首先,咱们创立了一个 DownloadAction 类,继承自 RecursiveAction 类,示意一个递归操作。在 compute 办法中,咱们首先判断以后操作的 URL 是否为一个,如果是,则间接调用 HttpUtil.download 办法进行文件下载。如果不是,则将 URL 列表分为两半,别离创立两个子工作进行解决,而后应用 invokeAll 办法将这两个子工作提交到线程池中并期待它们实现。

  在 main 办法中,咱们首先创立了一个 ForkJoinPool 对象,而后调用 invoke 办法来执行 DownloadAction 操作。在这里,咱们应用了默认的线程池,也能够依据须要创立自定义的线程池。

应用 Java 并行流

import java.util.Arrays;
import java.util.List;

public class ParallelStreamExample {public static void main(String[] args) {
        List<String> urls = Arrays.asList(
            "https://example.com/file1.txt", 
            "https://example.com/file2.txt", 
            "https://example.com/file3.txt", 
            "https://example.com/file4.txt", 
            "https://example.com/file5.txt"
        );
        urls.parallelStream().forEach(url -> HttpUtil.download(url));
        System.out.println("文件下载实现");
    }
}

  在这个示例中,咱们应用了 Java 并行流来实现文件下载。首先,咱们创立了一个 URL 列表,而后应用 parallelStream 办法将其转换为并行流。接着,咱们应用 forEach 办法遍历并行流中的每个 URL,并应用 HttpUtil.download 办法进行文件下载。在这个过程中,Java 会主动将并行流中的元素调配给多个线程并行执行,以进步程序的性能。

Java 并行流是什么?

  好了,置信看了下面的案例,应该对并行流有了一个简略的意识了吧。让本来又丑又长的代码,一下就变得眉清目秀了。所以那让咱们进一步的来理解它吧。

  Java 并行流是 Java 8 中新增的一个个性,它提供了一种便捷的形式来进行并发计算。在传统的 Java 编程中,为了利用多核处理器的性能,咱们须要手动编写多线程代码。然而多线程编程非常复杂,容易呈现死锁、竞态条件等问题,给咱们带来了很大的困扰。而 Java 并行流则提供了一种更加简略、易用、平安的并发编程形式,能够让咱们更加轻松地编写高效的并发程序。

  Java 并行流的外围是将数据汇合分成多个小块,而后在多个处理器上并行处理,最初将后果合并成一个后果集。应用 Java 并行流能够无效地利用多核处理器的性能,晋升程序运行效率。此外,Java 并行流还提供了一系列的两头操作和终止操作,能够不便地进行数据筛选、映射、过滤等操作。

Java 并行流的实现原理?

  Java 并行流是基于 Fork/Join 框架实现的,它应用了多线程来解决流操作。具体来说,Java 并行流的实现原理如下:

  1. 拆分数据

  当并行流操作开始时,数据会被拆分成多个小块。每个小块都会被调配给不同的线程去解决。

  1. 执行工作

  每个线程会独立地执行工作。线程会应用 fork/join 框架将本人的工作拆分成更小的子工作,并将这些子任务分配给其余线程。

  1. 合并后果

  当所有线程实现工作后,它们会将本人的后果合并到一起。这个过程相似于 reduce 操作,不同之处在于它是并行的。

  Java 并行流的是基于 Fork/Join 框架实现的,而 Fork/Join 框架是 Java 7 引入的一个用于并行计算的框架,它基于工作窃取算法,能够将一个大工作拆分成多个小工作,每个线程独立地解决一个小工作。在 Java 8 中,通过对 Stream 接口的扩大,使得并行计算更加容易实现。

  须要留神的是,Java 并行流在执行操作时,会依据以后计算机的 CPU 外围数来确定并行线程的数量,如果并行线程数量过多,会造成过多的上下文切换,反而会升高程序的性能。因而,在应用并行流时须要留神管制并行线程的数量。

三种形式比照

   在文件下载这个例子中,咱们应用了多线程、ForkJoin 框架和 Java 并行流三种形式来实现。咱们来比照一下这三种形式的优缺点。

1. 多线程形式

长处:

  • 能够手动控制线程的数量,实用于对线程数量有特殊要求的场景。
  • 能够应用线程池来重用线程,缩小线程创立和销毁的开销。
  • 能够应用 waitnotify 等机制来实现线程间的通信和合作。

毛病:

  • 须要手动编写线程的创立和销毁代码,代码复杂度较高。
  • 线程之间的合作和通信须要手动实现,容易呈现死锁等问题。
  • 代码的可读性和可维护性较差。

2. ForkJoin 框架形式

长处:

  • 能够主动地将工作拆分成更小的子工作,并将子任务分配给多个线程并行执行,简化了代码实现。
  • 能够通过调整并行度来优化性能,进步代码的灵活性。
  • 能够应用默认的线程池或自定义的线程池来治理线程。

毛病:

  • 不适用于 IO 密集型操作,仅实用于 CPU 密集型操作。
  • 线程之间的合作和通信须要手动实现,容易呈现死锁等问题。

3. Java 并行流形式

长处:

  • 能够应用函数式编程的形式简化代码实现,代码可读性较高。
  • 能够主动地将数据调配给多个线程并行处理,简化了代码实现。
  • 能够依据须要抉择并行度来优化性能。
  • 能够通过流水线形式优化代码性能,进步代码的灵活性。

毛病:

  • 不适用于 IO 密集型操作,仅实用于 CPU 密集型操作。
  • 对于一些非凡的操作,例如排序和去重,可能须要手动调整代码能力应用并行流。

总结

  Java 并行流能够让多线程编程变得更加简略易懂,缩小编程中的并发问题,进步代码品质和可维护性。帮忙开发人员更加轻松地实现工作并行,充分利用多核处理器的性能,放慢程序的执行速度。然而尽管并行流有诸多长处,然而还须要依据具体场景来抉择适合的形式。如果是 IO 密集型操作,咱们应该应用多线程或者 Java NIO 等技术来实现;如果是 CPU 密集型操作,咱们能够应用 ForkJoin 框架或者 Java 并行流来实现。

结尾

  如果感觉对你有帮忙,能够多多评论,多多点赞哦,也能够到我的主页看看,说不定有你喜爱的文章,也能够顺手点个关注哦,谢谢。

  我是不一样的科技宅,每天提高一点点,体验不一样的生存。咱们下期见!

正文完
 0