一次搞定多线程编程难题,让你的程序飞起来!

前言

  在日常的作业中,为了前进程序的处理速度,充分运用多核处理器的功能,咱们需求手动编写多线程代码。可是多线程编程非常复杂,简单呈现死锁、竞态条件等问题,给咱们带来了很大的困扰。而 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 并行流来完成。

结尾

  如果觉得对你有协助,能够多多谈论,多多点赞哦,也能够到我的主页看看,说不定有你喜爱的文章,也能够顺手点个重视哦,谢谢。

  我是不一样的科技宅,每天前进一点点,体验不一样的生活。咱们下期见!