JUC自学笔记02_real(Callable接口、读写锁、阻塞队列、线程池、合并分支、异步回调)

JUC自学笔记02_real(Callable接口、读写锁、阻塞队列、线程池、合并分支、异步回调)

一、Callable接口

(一)创建线程的方法

1、继承Thread类 2、实现Runnable接口 3、Callable接口 4、线程池方式

(二)Callable接口

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

//比较Runnable接口和Callable接口
//实现Runnable接口
class MyThread1 implements Runnable{
    @Override
    public void run() {

    }
}

class MyThread2 implements Callable {
    @Override
    public Integer call() throws Exception {
        return 200;
    }
}

public class Demo01 {
    public static void main(String[] args) {
        new Thread(new MyThread1(), "AA").start();

        //FutureTask
        //FutureTask<Integer> futureTask = new FutureTask<>(new MyThread2());

        //利用lam表达式来建立线程
        FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
            return 1024;
        });
        
        /**
         *
         * FutureTask原理 未来任务
         * 1、老师上课,口渴了,去买水不合适,讲课线程继续
         * 2、开启单独的线程:让另一个同学去帮忙买水,买来之后,老师要喝水的时候就可以去喝水了
         */
    }

}

(三)Callable接口的使用

//比较Runnable接口和Callable接口
//实现Runnable接口
class MyThread1 implements Runnable{
    @Override
    public void run() {

    }
}

class MyThread2 implements Callable {
    @Override
    public Integer call() throws Exception {
        return 200;
    }
}

public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        new Thread(new MyThread1(), "AA").start();

        //FutureTask
        //FutureTask<Integer> futureTask = new FutureTask<>(new MyThread2());

        //利用lam表达式来建立线程
        FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
            System.out.println(Thread.currentThread().getName().toString()+" come in callable");
            return 1024;
        });

        new Thread(futureTask2, "lucy").start();

        while (!futureTask2.isDone()){ //isDone()方法判断改线程是否结束
            System.out.println("wait ... ...");
        }

        System.out.println(futureTask2.get().toString());

        System.out.println(Thread.currentThread().getName()+" come over");
    }

}

二、JUC线程辅助类

(一)CountDownLatch类

在这里插入图片描述
概述:
CountDownLatch类可以设置一个计数器,然后通过countDown方法来进行减1的操作,使用await()方法等待计数器不大于0,然后继续执行await()方法之后的语句。

countDownLatch主要有两个方法,当一个或多个线程调用await方法的时候,这些线程会阻塞。
特点:

  • 其他线程调用countDown 方法会将计数器减1(调用CountDown方法的线程不会阻塞)。
  • 当计数器的值变成0的时候,因为await()方法阻塞的线程就会被环型,继续执行。

案例:六个同学都离开教室,值班同学才能关闭教室的门。

import java.util.concurrent.CountDownLatch;

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

        CountDownLatch countDownLatch = new CountDownLatch(6);

        for(int i = 0; i < 6; i++){
            new Thread(()->{
                //第i号同学离开教室
                System.out.println(Thread.currentThread().getName()+" 号同学离开了教室");

                //计数-1
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }

        //等待
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName().toString()+"班长锁门走人了");

    }
}

输出结果:

3 号同学离开了教室
2 号同学离开了教室
5 号同学离开了教室
4 号同学离开了教室
0 号同学离开了教室
1 号同学离开了教室
main班长锁门走人了

(二)循环栅栏CyclicBarrier

CyclicBarrier再使用中的构造方法第一个参数就是目标障碍数,每次执行CyclicBarrier一次障碍数就会加1,如果达到第一个参数即目标障碍数的值,才会执行构造函数的第二个参数代表的方法,或者是CyclicBarrier.await()方法之后的程序。
在这里插入图片描述

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {

    private static final int NUMBER = 7;


    public static void main(String[] args) {

        //创建CyclicBarrier :设置固定值,当达到固定值的时候,就进行下面的程序
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ()->{
            System.out.println("hello World");
        });

        //达到固定值的过程
        for(int i = 0; i <= 7; i++){
            new Thread(()->{
                try{
                    System.out.println(Thread.currentThread().getName());
                    //等待
                    cyclicBarrier.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }

    }
}

输出结果:

0
5
3
2
1
4
7
6
hello World

(三)信号灯Semaphore

在这里插入图片描述
在这里插入图片描述
案例:6辆汽车,停三个车位

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

//6辆汽车,停三个车位
public class SemaphoreTest {

    private static final int NUMBER = 3;

    public static void main(String[] args) {
        //创建Semaphore 设置蓄客量为3
        Semaphore semaphore = new Semaphore(NUMBER);

        //模拟6辆车
        for (int i = 0; i < 6; i++){
            new Thread(()->{
                try{
                    //抢占
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到了车位");
                    //设置随机停车时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                    //离开车位
                    System.out.println(Thread.currentThread().getName()+"离开===========");
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

三、读写锁

(一)锁的类型

1、悲观锁、乐观锁
2、表锁、行锁
3、读锁(共享锁)、写锁(独占锁)
读锁和写锁都会发生死锁:原因:

(二)使用读写锁之前

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

//资源类
class MyCache{
    //创建map集合
    private volatile Map<String, Object> map = new HashMap<>();

    //放数据
    public void put(String key, Object val) throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+"这个线程正在进行写操作");

        //暂停一会
        try{
            TimeUnit.MICROSECONDS.sleep(300);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        //进行村方数据的操作
        map.put(key, val);
        System.out.println(Thread.currentThread().getName()+"写完了"+key);
    }

    //取数据
    public Object get(String key) throws InterruptedException {
        Object result = null;
        System.out.println(Thread.currentThread().getName()+"正在进行读取数据的操作"+key);

        //暂停一会
        try{
            TimeUnit.MICROSECONDS.sleep(300);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        result = map.get(key);
        System.out.println(Thread.currentThread().getName()+"取完数据了"+key);
        return result;
    }
}


public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        //创建线程存放数据
        for(int i = 1; i <= 5; i++){
            final int num = i;
            new Thread(()->{
                try {
                    myCache.put(num+"", num+"");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }

        //创建线程取数据
        for(int i = 1; i <= 5; i++){
            final int num = i;
            new Thread(()->{
                try {
                    myCache.get(num+"");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:

5这个线程正在进行写操作
2这个线程正在进行写操作
4这个线程正在进行写操作
1这个线程正在进行写操作
3这个线程正在进行写操作
3写完了3
5正在进行读取数据的操作5
3正在进行读取数据的操作3
1写完了1
5写完了5
4正在进行读取数据的操作4
4写完了4
2正在进行读取数据的操作2
2写完了2
1正在进行读取数据的操作1
5取完数据了5
3取完数据了3
4取完数据了4
2取完数据了2
1取完数据了1

Process finished with exit code 0

(三)使用读写锁之后

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

//资源类
class MyCache{
    //创建map集合
    private volatile Map<String, Object> map = new HashMap<>();

    //创建读写锁
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //放数据
    public void putKey(String key, Object val){
        rwLock.writeLock().lock(); //添加写锁
        try{
            System.out.println(Thread.currentThread().getName()+"这个线程正在进行写操作");
            //暂停一会

            TimeUnit.MICROSECONDS.sleep(300);
            //进行村方数据的操作
            map.put(key, val);
            System.out.println(Thread.currentThread().getName()+"写完了"+key);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            rwLock.writeLock().unlock(); //释放写锁
        }
    }

    //取数据
    public Object getVal(String key) throws InterruptedException {

        rwLock.readLock().lock(); //添加读锁
        Object result = null;
        try{
            System.out.println(Thread.currentThread().getName()+"正在进行读取数据的操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName()+"取完数据了"+key);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            rwLock.readLock().unlock(); //释放读锁
        }
        return result;
    }
}

public class ReadWriteLockTest {
    public static void main(String[] args) throws InterruptedException {
        MyCache myCache = new MyCache();
        //创建线程存放数据
        for(int i = 1; i <= 5; i++){
            final int num = i;
            new Thread(()->{
                try {
                    myCache.putKey(num+"", num+"");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }

        TimeUnit.SECONDS.sleep(3);

        //创建线程取数据
        for(int i = 1; i <= 5; i++){
            final int num = i;
            new Thread(()->{
                try {
                    myCache.getVal(num+"");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:

5这个线程正在进行写操作
5写完了5
2这个线程正在进行写操作
2写完了2
1这个线程正在进行写操作
1写完了1
3这个线程正在进行写操作
3写完了3
4这个线程正在进行写操作
4写完了4
1正在进行读取数据的操作1
5正在进行读取数据的操作5
2正在进行读取数据的操作2
3正在进行读取数据的操作3
4正在进行读取数据的操作4
4取完数据了4
2取完数据了2
5取完数据了5
3取完数据了3
1取完数据了1

Process finished with exit code 0

结论:
读锁可以多个线程同时进行读取,但是添加写锁的时候不能多个线程同时进行写操作。

读写互斥、读读共享、写写互斥

(四)读写锁特点:

优点:
相比于Synchronized关键字,可以进行合多个读取数据的线程同时对数据进行读取操作,可以大大提高性能,同时多个线程可以进行读操作,但是写的操作与Synchronized关键字是一样的。
缺点:
(1)可能会造成锁饥饿,就是一直进行读操作,但是一致没有写操作。
(2)读的线程只能进行读操作,不能进行写操作,但是进行写的线程的时候,再写的i线程里可以进行读的操作。

(五)读写锁的降级

主要指的是:将写入的锁降级为读锁
锁降级是指把持住当前拥有的写锁的同时,再获取到读锁,随后释放写锁的过程。
在这里插入图片描述
以下是oracle官网的对于锁降级的示例代码:

 class CachedData {
   Object data;
   volatile boolean cacheValid;
   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        try {
          // Recheck state because another thread might have
          // acquired write lock and changed state before we did.
          if (!cacheValid) {
            data = ...
            cacheValid = true;
          }
          // Downgrade by acquiring read lock before releasing write lock
          rwl.readLock().lock();
        } finally {
          rwl.writeLock().unlock(); // Unlock write, still hold read
        }
     }

     try {
       use(data);
     } finally {
       rwl.readLock().unlock();
     }
   }
 }

代码中声明了一个volatile类型的cacheValid变量,保证其可见性。首先获取读锁,如果cache不可用,则释放读锁,获取写锁,在更改数据之前,再检查一次cacheValid的值,然后修改数据,将cacheValid置为true,然后在释放写锁前获取读锁;此时,cache中数据可用,处理cache中数据,最后释放读锁。这个过程就是一个完整的锁降级的过程,目的是保证数据可见性,如果当前的线程C在修改完cache中的数据后,没有获取读锁而是直接释放了写锁,那么假设此时另一个线程T获取了写锁并修改了数据,那么C线程无法感知到数据已被修改,则数据出现错误。如果遵循锁降级的步骤,线程C在释放写锁之前获取读锁,那么线程T在获取写锁时将被阻塞,直到线程C完成数据处理过程,释放读锁。

四、BlockingQueue阻塞队列

(一)概述

阻塞队列:首先是一个队列,通过一个共享的队列,可以使得数据由队列的一端输入,另一端输出。
在这里插入图片描述
当队列是空的,从队列中获取元素的操作将会被阻塞;
当队列是满的,从队列中添加元素的操作将会被阻塞;
试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素;
试图向已满的队列中添加新元素的线程将会被阻塞,直到其他线程从队列中移除一个或多个元素或者完全清空,使队列变得空闲起来并后续新增。

(二)阻塞队列的分类

https://www.yuque.com/racek/note.book/vbi67e?inner=ebDlc

(三)阻塞队列中常用的方法:

在这里插入图片描述
在这里插入图片描述
方法的代码演示

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

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

        //创建一个阻塞队列
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        //第一组方法的演示
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        //System.out.println(blockingQueue.element());
        //System.out.println(blockingQueue.add(w)); //超过队列长度,会抛异常

        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //System.out.println(blockingQueue.remove()); //队列已经是空的了,会抛异常

        //第二组方法的演示
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("ww"));

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());

        //第三组方法的演示
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //blockingQueue.put("www");

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());

        //第四组方法的演示
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("www", 3L, TimeUnit.SECONDS));

    }
}

五、线程池

(一)基本概述

线程池(英语:thread pool ) :一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而设置线程池,让线程池维护多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。

线程池的优势︰
线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

线程池的特点:

  • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
  • 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
  • Java 中的线程池是通过Execu tor框架实现的,该框架中用到了Execu tor,Executors ,ExecutorService ,ThreadPooIExecu tor这几个类

(二)线程池的使用方式

1、Executors.newFixedThreadPool(int)这是一池多线程
2、Executors.newSingleThreadExecutors()这是一池一线程
3、Executors.newCachedThreadPool()这是根据实际需求进行线程的创建

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//线程池的三种分类
public class ThreadPool {
    public static void main(String[] args) {
        //================一池多线程==================
        //一池5线程
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //五个线程

        try{
            //10个线程需求
            for (int i = 0; i < 10; i++){
                //执行
                threadPool1.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"正在使用线程");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool1.shutdown();
        }


        //======================一池一线程==========================
        //一池5线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        try{
            //10个线程需求
            for (int i = 0; i < 10; i++){
                //执行
                threadPool2.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"正在使用线程");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool2.shutdown();
        }

        //=====================一池可扩容线程=================================
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        try{
            //10个线程需求
            for (int i = 0; i < 20; i++){
                //执行
                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"正在使用线程");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool3.shutdown();
        }
    }
}

(三)他们的底层都是用到了同一个类:ThreadPoolExecutor

//=================一池多线程====================
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
//======================一池一线程==========================
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
//===============一池可扩容线程================
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

ThreadPoolExecutor类的构造器的的七个参数含义:

在这里插入图片描述

(四)线程池的工作流程

在这里插入图片描述

(五)线程池的拒绝策略

1、AbortPolicyt默认)∶直接抛出RejectedExecutionException异常阻止系统正常运行

2、CallerRunsPolicy:调用者运行 —种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。

3、DiscardoldestPolicy :抛弃队列中等待最久的任务,然后把当前任务加人队列中尝试再次提交当前任务。

4、DiscardPolicy :该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的—种策略。

(六)自定义线程池

一般在项目中三种线程池都不用,而是利用ThreadPoolExecutor类自定义线程池。
该类的构造方法:

public ThreadPoolExecutor(int corePoolSize,//常驻线程的数量
                              int maximumPoolSize, //最大线程数量
                              long keepAliveTime, //存活时间
                              TimeUnit unit, //存活时间的单位
                              BlockingQueue<Runnable> workQueue, //阻塞队列
                              ThreadFactory threadFactory, //线程工厂,主要用来创建线程
                              RejectedExecutionHandler handler) {//拒绝策略,四种
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

自定义一个线程池,然后进行案例使用:

import java.util.concurrent.*;

public class ThreadDemo02 {
    public static void main(String[] args) {
        //===================自己定义一个线程池===============
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                2L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        //===============使用线程池===============
        try{
            //10个线程需求
            for (int i = 0; i < 20; i++){
                //执行
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"正在使用线程");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

运行结果:

java.util.concurrent.RejectedExecutionException: Task com.atguigu.ThirdCap.ThreadDemo02$$Lambda$14/0x0000000800066840@67b64c45 rejected from java.util.concurrent.ThreadPoolExecutor@4411d970[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]
	at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2055)
	at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:825)
	at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1355)
	at com.atguigu.ThirdCap.ThreadDemo02.main(ThreadDemo02.java:23)
pool-1-thread-5正在使用线程
pool-1-thread-2正在使用线程
pool-1-thread-1正在使用线程
pool-1-thread-3正在使用线程
pool-1-thread-1正在使用线程
pool-1-thread-4正在使用线程
pool-1-thread-3正在使用线程
pool-1-thread-5正在使用线程

六、分支合并框架Fork/Join框架

(一)简介

Fork/join可以将一个大的任务拆分成多个子任务及逆行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。
Fork:把一个复杂任务进行拆分,大事化小;
Join:把拆分的任务的计过进行合并。

执行过程:
1、任务分割:首先Fork/Join框架需要把大的任务分割成足够小的子任务,如果子任务比较大的话,还要对子任务进行分割;
2、执行任务并合并结果:分割的子任务分别放到两端的队列里面,然后几个启动线程分别从双端队列里面获取任务执行,子任务执行完的结果存放到另一个队列中,启动一个线程从队列里面获取数据,然后合并这些数据。

(二)Fork/Join的使用

案例:利用分支合并框架进行0到100的相加,并且如果拆分的时候,两端数字的差值大于10,就必须继续及逆行拆分,知道两端 数字差值小于10,才能进行相加操作。

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

class MyTask extends RecursiveTask<Integer>{

    //拆分的时候,差值不能超过10
    private static final int VALUE = 10;
    private int begin;
    private int end;
    private int result;

    public MyTask(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    @Override
    protected Integer compute() {

        //判断相加的两个数值是否差值大于10
        if(end - begin <= 10){
            for(int i = begin; i <= end; i++){
                result += i;
            }
        }else {
            int middle = (end + begin) / 2;
            //下面进行递归
            MyTask task01 = new MyTask(begin, middle);
            MyTask task02 = new MyTask(middle+1, end);
            //拆分
            task01.fork();
            task02.fork();
            //合并
            result = task01.join() + task02.join();
        }

        return result;
    }
}

public class ForkJoinTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        MyTask myTask = new MyTask(0, 100);
        //创建分支合并池对象
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        //从分支合并池里面创建分支合并对象
        ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);
        //使用对象进行0到100的相加,并输出结果
        Integer result = forkJoinTask.get();
        System.out.println(result);
        //关闭分支合并池对象
        forkJoinPool.shutdown();
    }
}

七、异步回调CompletableFuture

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //异步调用,没有返回值
        CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName()+"异步调用,无返回值");
        });
        completableFuture1.get();


        //异步调用,没有返回值
        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"异步调用,有返回值");
            return 1024;
        });

        completableFuture2.whenComplete((t, u)->{
            System.out.println("t= "+t); //返回值
            System.out.println("u= "+u); //输出的异常
        }).get();

    }

}

输出结果:

ForkJoinPool.commonPool-worker-3异步调用,无返回值
ForkJoinPool.commonPool-worker-3异步调用,有返回值
t= 1024
u= null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仲子_real

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值