美文网首页程序员
来吧,展示!这份阿里P7大佬给我的JUC知识总结真的写的太详细了

来吧,展示!这份阿里P7大佬给我的JUC知识总结真的写的太详细了

作者: 程序员匡胤 | 来源:发表于2020-08-19 20:55 被阅读0次

    前言

    在 Java 5.0 提供了 java.util.concurrent (简称 JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。 提供可调的、灵活的线程池。还提供了设计用于 多线程上下文中的 Collection 实现等

    volatile 关键字-内存可见性

    JVM为每一个线程提供一个独立的缓存,用于提高效率

    内存可见性(Memory Visibility)是指当某个线程正在使用对象状态 而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化

    可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能实时地看到其他线程写入的值,有时甚至是不可能的事情

    我们可以通过同步来保证对象被安全地发布。除此之外我们也可以使用一种更加轻量级的volatile变量

    Java 提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作通知到其他线程。可以将 volatile 看做一个轻量级的锁,但是又与锁有些不同:

    • 对于多线程,不是一种互斥关系
    • 不能保证变量状态的 “原子性操作”
    public class VolatileTest {
        public static void main(String[] args) {
            ThreadDemo t = new ThreadDemo();
            new Thread(t).start();
            while (true) {
                if(t.isFlag()) {
                    System.out.println("------");
                    break;
                }
            }
        }
    }
    class ThreadDemo implements Runnable {
        private boolean flag = false;
        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            System.out.println("flag = " + isFlag());
        }
    
        public boolean isFlag() {
            return flag;
        }
    
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    }
    
    

    mian线程读取到的flag 一直都是false,所以打印结果为 flag = true,然后程序没有结束

    解决:

    volatile,当多个线程进行操作共享数据时,可以保证内存中的数据可见


    public class VolatileTest {
        public static void main(String[] args) {
            ThreadDemo t = new ThreadDemo();
            new Thread(t).start();
            while (true) {
                if(t.isFlag()) {
                    System.out.println("------");
                    break;
                }
            }
        }
    }
    class ThreadDemo implements Runnable {
        private volatile boolean flag = false;
        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            System.out.println("flag = " + isFlag());
        }
        public boolean isFlag() {
            return flag;
        }
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    }
    
    

    原子变量-CAS算法

    类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可 将 volatile 值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类。

    类 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的实例各自提供对 相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。

    AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 类进一步扩展了原子操 作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方 面也引人注目,这对于普通数组来说是不受支持的。

    核心方法:boolean compareAndSet(expectedValue, updateValue)

    java.util.concurrent.atomic 包下提供了一些原子操作的常用类: AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference AtomicIntegerArray 、AtomicLongArray ,AtomicMarkableReference ,AtomicReferenceArray ,AtomicStampedReference
    具体的一些方法,可查看API文档

    i++ 原子性问题,先读取到i 然后再 ++ ,操作被分开了,有同步安全问题

    public class AtomicTest {
        public static void main(String[] args) {
            Atomic a = new Atomic();
            for (int i = 0; i < 10; i++) {
                new Thread(a).start();
            }
        }
    }
    class Atomic implements Runnable {
        private volatile int serialNumber = 0;
        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber());
        }
        public int getSerialNumber() {
            return serialNumber++;
        }
    }
    
    

    原子变量 jdk1.5后 java.util.concurrent.atomic包下提供了常用的原子变量

    • 1.volatile 保证内存可见性

    • 2.CAS算法 保证数据的原子性

      CAS算法是硬件对于并发操作共享数据的支持

      包含了三个操作数:

      内存值 V

      预估值 A

      更新值 B

      当且仅当 V == A时,V = B,否则,将不做任何操作

    模拟CAS

    public class CompareAndSwapTest {
        public static void main(String[] args) {
            CompareAndSwap cas = new CompareAndSwap();
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
    
                    @Override
                    public void run() {
                        int expectedValue = cas.get();
                        boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
                        System.out.println(b);
                    }
                }).start();
            }
        }
    }
    class CompareAndSwap {
        private int value;
        // 获取内存值
        public synchronized int get(){
            return value;
        }
        // 比较
        public synchronized int compareAndSwap(int expectedValue, int newValue) {
            int oldValue = value;
            if(oldValue == expectedValue) {
                this.value = newValue;
            }
            return oldValue;
        }
        // 设置
        public synchronized boolean compareAndSet(int expectedValue, int newValue) {
            return expectedValue == compareAndSwap(expectedValue, newValue);
        }
    }
    
    

    解决 i++原子性问题

    public class AtomicTest {
        public static void main(String[] args) {
            Atomic a = new Atomic();
            for (int i = 0; i < 10; i++) {
                new Thread(a).start();
            }
        }
    }
    class Atomic implements Runnable {
        private AtomicInteger serialNumber = new AtomicInteger();
        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(getSerialNumber());
        }
        public int getSerialNumber() {
            return serialNumber.getAndIncrement();
        }
    }
    
    

    ConcurrentHashMap 锁分段机制

    Hashtable效率非常低 复合操作可能线程不安全 一次只能一个线程进行操作

    复合操作包括迭代(反复获取元素,直到容器中的最后一个元素)、导航(根据一定顺序查找下一元素)、条件运算(“若不存在则添加”,“若存在则删除”)

    Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器 的性能

    ConcurrentHashMap 同步容器类是Java 5 增加的一个线程安全的哈希表。对 与多线程的操作,介于 HashMap 与 Hashtable 之间。内部采用“锁分段” 机制替代 Hashtable 的独占锁。进而提高性能

    此包还提供了设计用于多线程上下文中的 Collection 实现: ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、 CopyOnWriteArrayList 和 CopyOnWriteArraySet。当期望许多线程访问一个给 定 collection 时,ConcurrentHashMap 通常优于同步的 HashMap, ConcurrentSkipListMap 通常优于同步的 TreeMap。当期望的读数和遍历远远 大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList

    ConcurrentHashMap 的 concurrentLevel 是16


    每个段都是独立的锁,当多个线程并发访问时,在不同的段上进行操作,则可做到并行

    copyOnWriteArrayList例子

    public class CopyOnWriteArrayListTest {
        public static void main(String[] args) {
            HelloThread ht = new HelloThread();
            for (int i = 0; i < 2; i++) {
                new Thread(ht).start();
            }
        }
    }
    /**
     * CopyOnWriteArrayList写入并复制,添加操作多时,效率低,因为每次添加时都会进行复制,开销很大
     * 并发迭代操作多时可以选择
     */
    class HelloThread implements Runnable {
        // 这种会出现并发修改异常
    //    private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
        private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        static {
            list.add("AA");
            list.add("BB");
            list.add("CC");
        }
        @Override
        public void run() {
            Iterator<String> it = list.iterator();
            while(it.hasNext()){
                System.out.println(it.next());
                list.add("AA");
            }
        }
    }
    
    

    CountDownLatch 闭锁

    Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器 的性能。

    CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作 之前,它允许一个或多个线程一直等待。

    闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活 动直到其他活动都完成才继续执行:

    确保某个计算在其需要的所有资源都被初始化之后才继续执行;
    确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
    等待直到某个操作所有参与者都准备就绪再继续执行

    闭锁:在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才能继续执行

    public class CountDownLatchTest {
        public static void main(String[] args) {
            // 5 表示其他线程的数量
            CountDownLatch latch = new CountDownLatch(5);
            LatchDemo ld = new LatchDemo(latch);
            long start = System.currentTimeMillis();
            for (int i = 0; i < 5; i++) {
                new Thread(ld).start();
            }
            try {
                // 此处要一直等到 latch的值为0 ,就能往下执行了
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end = System.currentTimeMillis();
            System.out.println("消耗时间为:" + (end - start));
        }
    }
    class LatchDemo implements Runnable {
        private CountDownLatch latch;
        public LatchDemo(CountDownLatch latch) {
            this.latch = latch;
        }
        @Override
        public void run() {
            synchronized(this) {
                try {
                    for (int i = 0; i < 100; i++) {
                        if (i % 2 == 0) {
                            System.out.println(i);
                        }
                    }
                } finally {
                    latch.countDown();
                }
            }
        }
    }
    
    

    Condition 控制线程通信

    Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用 法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的 功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关 联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版 本中的不同

    在 Condition 对象中,与 wait、notify 和 notifyAll 方法对应的分别是 await、signal 和 signalAll

    Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    // 三个方法的使用方法和 wait,notify 和 notifyAll一样
    
    

    线程按序交替

    编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要 求输出的结果必须按顺序显示。 如:ABCABCABC…… 依次递归

    public class ABCAlternateTest {
        public static void main(String[] args) {
            AlternateDemo ad = new AlternateDemo();
            new Thread(new Runnable() {
                @Override
                public void run(){
                    for (int i = 1; i <= 10; i++) {
                        ad.loopA(i);
                    }
                }
            },"A").start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10; i++) {
                        ad.loopB(i);
                    }
                }
            },"B").start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10; i++) {
                        ad.loopC(i);
                    }
                }
            },"C").start();
        }
    }
    class AlternateDemo {
        // 记录当前正在执行的线程的ID
        private int number = 1;
        private Lock lock = new ReentrantLock();
        private Condition condition1 = lock.newCondition();
        private Condition condition2 = lock.newCondition();
        private Condition condition3 = lock.newCondition();
    
        public void loopA(int totalLoop) {
            try {
                lock.lock();
                if(number != 1) {
                    try {
                        condition1.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 打印
                for (int i = 1; i <= 1; i++) {
                    System.out.print(Thread.currentThread().getName());
                }
                number = 2;
                condition2.signal();
            } finally {
                lock.unlock();
            }
        }
        public void loopB(int totalLoop) {
            try {
                lock.lock();
                if(number != 2) {
                    try {
                        condition2.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 打印
                for (int i = 1; i <= 1; i++) {
                    System.out.print(Thread.currentThread().getName());
                }
                number = 3;
                condition3.signal();
            } finally {
                lock.unlock();
            }
        }
        public void loopC(int totalLoop) {
            try {
                lock.lock();
                if(number != 3) {
                    try {
                        condition3.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 打印
                for (int i = 1; i <= 1; i++) {
                    System.out.print(Thread.currentThread().getName());
                }
                number = 1;
                condition1.signal();
                System.out.print(" ");
            } finally {
                lock.unlock();
            }
        }
    }
    
    

    ReadWriteLock 读写锁

    ReadWriteLock是一个接口

    ReadWriteLock 维护了一对相关的锁,一个用于只读操作, 另一个用于写入操作。只要没有 writer,读取锁可以由 多个 reader 线程同时保持。写入锁是独占的

    ReadWriteLock 读取操作通常不会改变共享资源,但执行 写入操作时,必须独占方式来获取锁。 对于读取操作占 多数的数据结构。ReadWriteLock 能提供比独占锁更高 的并发性。而对于只读的数据结构,其中包含的不变性 可以完全不需要考虑加锁操作

    • 写写 / 读写 都需要“互斥”
    • 读读 不需要互斥
    public class ReadWriteLockTest {
        public static void main(String[] args) {
            ReadWriteLockDemo rw = new ReadWriteLockDemo();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    rw.set((int)(Math.random() * 101));
                }
            },"write").start();
            for (int i = 0; i < 100; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        rw.get();
                    }
                }).start();
            }
        }
    }
    class ReadWriteLockDemo {
        private int number = 0;
        private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        // 读
        public void get() {
            try {
                readWriteLock.readLock().lock();
                System.out.println(Thread.currentThread().getName() + ":" + number);
            } finally {
                readWriteLock.readLock().unlock();
            }
        }
        // 写
        public void set(int number) {
            try {
                readWriteLock.writeLock().lock();
                System.out.println(Thread.currentThread().getName());
                this.number = number;
            } finally {
                readWriteLock.writeLock().unlock();
            }
        }
    }
    
    

    线程八锁

    静态同步方法与非静态同步方法之间是不会有竞态条件的,非静态同步方法,看他们的锁是不是同一个number,是同一个number则,一个拿到,另一个要等待;否则,另一个不用等待

    * 线程八锁的关键: 1.非静态方法的锁 this, 静态方法的锁 对应的Class实例
     *               2.某一个时刻内,只能由一个线程持有锁,无论几个方法
     * 1.两个同步方法,两个线程,打印 one  two
     * 2.新增Thread.sleep()给getOne 打印 one two
     * 3.新增普通方法getThree,打印  three one two
     * 4.注释getThree,number2.getTwo,打印 two one
     * 5.修改getOne为静态同步方法,改为number.getTwo,打印 two one
     * 6.两个方法都为静态同步方法,一个number对象,打印 one two
     * 7.getOne为静态同步方法,getTwo为同步方法,改为number2.getTwo,打印two one
     * 8.两个静态同步方法,两个number对象,打印 one two
     */
    public class Thread8MonitorTest {
        public static void main(String[] args) {
            Number number = new Number();
            // 4
    //        Number number2 = new Number();
            // 7
            Number number2 = new Number();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    number.getOne();
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 1,2,3
    //                number.getTwo();
                    // 4
    //                number2.getTwo();
                    // 5
    //                number.getTwo();
                    // 7
                    number2.getTwo();
                }
            }).start();
            // 3
            /*new Thread(new Runnable() {
                @Override
                public void run() {
                    number.getThree();
                }
            }).start();*/
    
        }
    }
    class Number{
        public static synchronized void getOne() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("one");
        }
        public static synchronized void getTwo() {
            System.out.println("two");
        }
        public void getThree() {
            System.out.println("three");
        }
    }
    
    

    线程池

    第四种获取线程的方法:线程池,一个 ExecutorService,它使用可能的几个池线程之
    一执行每个提交的任务,通常使用 Executors 工厂方法配置。

    线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在
    执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行
    任务集时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数
    据,如完成的任务数。

    为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展钩子 (hook)。但
    是,强烈建议程序员使用较为方便的 Executors 工厂方法 :

    • Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)
    • Executors.newFixedThreadPool(int)(固定大小线程池)
    • Executors.newSingleThreadExecutor()(单个后台线程)
      它们均为大多数使用场景预定义了设置。

    思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交 通工具。

    好处: 1. 提高响应速度(减少了创建新线程的时间)

    2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)

    3.便于线程管理 corePoolSize:核心池的大小 ,/maximumPoolSize:最大线程数,keepAliveTime:线程没有任务时最多保持多长时间后会终止 线程池:提供了一个线程队列,队列中保存着所有等待状态的线程,避免了创建于销毁的额外开销,提高了响应的速度

    java.util.concurrent.Executor :负责线程的使用与调度的根接口
    –ExecutorService 子接口:线程池的主要接口,继承Executor
    –ThreadPoolExecutor 线程池的实现类
    –ScheduledExecutorService 子接口:负责线程的调度,继承ExecutorService
    –ScheduledThreadPoolExecutor 继承ThreadPoolExecutor 实现ScheduledExecutorService

    public class ThreadPoolTest {
        public static void main(String[] args) {
    
            // 1.创建线程池 5个线程
            ExecutorService pool = Executors.newFixedThreadPool(5);
            ThreadPoolDemo tpd = new ThreadPoolDemo();
            /*// 2.为线程池中的线程分配任务
            for (int i = 0; i < 5; i++) {
                pool.submit(tpd);
            }
            // 3.关闭线程池,保证所有线程的任务完成才会关闭
            // shutdownNow() 立即关闭,不管任务做完没有
            pool.shutdown();*/
            List<Future<Integer>> list = new ArrayList<>();
            for (int i = 0; i < 5; i++) {
            // Future得到Callable的返回值
                Future<Integer> future = pool.submit(new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
                        int sum = 0;
                        for (int j = 0; j <= 100; j++) {
                            sum += j;
                        }
                        return sum;
                    }
                });
                list.add(future);
            }
            pool.shutdown();
            for(Future<Integer> future : list) {
                try {
                    System.out.println(future.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    class ThreadPoolDemo implements Runnable{
        private int i = 0;
        @Override
        public void run() {
            while(i <= 100){
                System.out.println(Thread.currentThread().getName() + " : " + i++);
            }
        }
    }
    
    

    线程调度

    ScheduledExecutorService newScheduledThreadPool() 创建固定大小的线程,可以延迟或定时的执行任务

    public class ScheduledThreadPoolTest {
        public static void main(String[] args) {
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
            for (int i = 0; i < 5; i++) {
                Future<Integer> result = pool.schedule(new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
                        int num = new Random().nextInt(100);
                        System.out.println(Thread.currentThread().getName() + ":" + num);
                        return num;
                    }
                }, 3, TimeUnit.SECONDS);// 延迟3s执行
                try {
                    System.out.println(result.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
            pool.shutdown();
        }
    }
    
    

    TimeUnit的用法

    ForkJoinPool 分支/合并框架 工作窃取

    Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成 若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进 行 join 汇总

    采用 “工作窃取”模式(work-stealing): 当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加 到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队 列中

    相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务 的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些 原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中, 如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理 该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了 线程的等待时间,提高了性能

    Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换

    public class ForkJoinPoolTest {
        public static void main(String[] args) {
            Instant start = Instant.now();
            ForkJoinPool pool = new ForkJoinPool();
            // RecursiveTask<V> extends ForkJoinTask<V>
            ForkJoinTask<Long> task = new ForkJoinTest(0L, 50000000000L);
            Long sum = pool.invoke(task);
            System.out.println(sum);
            Instant end = Instant.now();
            System.out.println(Duration.between(start, end).toMillis());
        }
        @Test
        public void test2() { // 18256
            Instant start = Instant.now();
            long sum = 0;
            for (long i = 0; i <= 50000000000L; i++) {
                sum += i;
            }
            Instant end = Instant.now();
            System.out.println(Duration.between(start, end).toMillis());
        }
        @Test
        public void test3() { // 14444
            //  对ForkJoin的改进
            Instant start = Instant.now();
            // rangeClosed 生成连续的数
            long sum1 = LongStream.rangeClosed(0, 50000000000L)
                    .parallel()
                    .reduce(0, Long::sum); // 第二个参数 是函数式接口LongBinaryOperator
            Instant end = Instant.now();
            System.out.println(Duration.between(start, end).toMillis());
        }
    }
    class ForkJoinTest extends RecursiveTask<Long> {// Recursive 递归
        // RecursiveAction 没有返回值
        // RecursiveTask 有返回值
        private long start;
        private long end;
    
        public ForkJoinTest(long start, long end) {
            this.start = start;
            this.end = end;
        }
        private static final long THRESHOLD = 10000;
    
        @Override
        protected Long compute() {
            long length = end - start;
            if(length <= THRESHOLD){
                long sum = 0;
                for (long i = start; i <= end; i++) {
                    sum += i;
                }
                return sum;
            } else {
                long middle = (start + end) / 2;
                ForkJoinTest left = new ForkJoinTest(start, middle);
                left.fork(); // 拆分子任务,同时压入线程队列
                ForkJoinTest right = new ForkJoinTest(middle + 1, end);
                right.fork();
                return left.join() + right.join();
            }
        }
    }
    

    最后

    感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

    相关文章

      网友评论

        本文标题:来吧,展示!这份阿里P7大佬给我的JUC知识总结真的写的太详细了

        本文链接:https://www.haomeiwen.com/subject/agwdjktx.html