多线程学习笔记

多线程学习笔记

这里写目录标题

  • 多线程学习笔记
  • 多线程基础
  • 一、进程与线程
    • 1.1 进程与线程的概念
    • 1.2 线程状态
  • 二、多线程
    • 2.1 概念区分
    • 2.2 多线程编程应用场景
  • 三、线程编程
    • 3.1 线程的创建方式
    • 3.2 线程优先级
    • 3.3 关键字和方法讲解
    • 3.3 线程停止方式
    • 3.4 多线程编程步骤
    • 3.5 生产者消费者模式
  • JUC并发编程
  • 一、JUC简介
    • 1、什么是JUC:
    • 2、线程Thread的状态
    • 3、管程
    • 4、用户线程和守护线程
  • 二、Lock接口
  • 三、虚假唤醒
  • 四、线程定制化通信
    • 4.1 Condition条件队列
    • 4.2 实现方式
  • 五、集合的线程安全
    • 列表的线程不安全
    • HashSet线程不安全
    • HashMap线程不安全
  • 六、多线程锁
    • 6.1 死锁
    • 6.2 公平锁和非公平锁
    • 6.3 可重入锁和不可重入锁
    • 6.4 乐观锁和悲观锁
    • 6.5 读写锁
    • 6.6 锁降级
  • 七、Callable接口
  • 八、辅助类
    • 8.1 减少计数CountDownLatch
    • 8.2 循环栅栏CyclicBarrier
    • 8.3 信号灯Semaphore
  • 十、阻塞队列
    • 10.1 概述
    • 10.2 常见的BlockingQueue
    • 10.3 核心方法
  • 十一、线程池
    • 11.1 线程池的框架
    • 11.2 线程池的使用方式
      • newFixedThreadPool
      • newSingleThreadExecutor
      • newCachedThreadPool
    • 11.3 线程池参数讲解
    • 11.4 拒绝策略
    • 11.5 自定义线程池
  • 十二、Fork/Join框架
    • 12.1 Fork/Join框架简介
    • 12.2 使用案例
  • 十三、CompletableFuture

多线程基础

一、进程与线程

1.1 进程与线程的概念

  • 进程是计算机的程序关于某数据集合的一次运行活动

  • 进程是资源分配的最小单位,线程是资源调度的最小单位

  • 线程是进程中一个独立的执行流

**进程和程序:**程序是指令、数据及其组织形式的描述,进程是程序的实体

1.2 线程状态

线程状态图:

多线程学习笔记_第1张图片

五个状态:

  • 新建:new创建后到start方法前
  • 就绪:调用start方法后就是处于就绪态
  • 运行:获取CPU资源,执行run方法,此时就是运行态
  • 阻塞:
    • 同步阻塞:synchronized获取同步锁失败阻塞
    • 等待阻塞:执行wait方法后
    • 其他阻塞:sleep,join方法执行后
  • 死亡:线程执行结束,或其他条件终止

二、多线程

2.1 概念区分

并发与并行:

  • 并发:一个时间段内,几个任务同时进行。
  • 并行:同一时间点,几个任务同时进行,必要条件:多核,多处理器。

同步与异步:

  • 同步:任务有序执行,一个任务执行完,再执行下一个任务。
  • 异步:几个任务独立执行,执行一个任务的同时也可以执行另一个任务。

异步与多线程:

  • 异步是目的,多线程是实现异步的一种手段

什么是多线程:

  • 是指从软件或者硬件上实现多个线程并发执行的技术

2.2 多线程编程应用场景

  • 高并发
  • 处理大任务:分步上传,分步计算
  • 耗时操作
  • 低优先级定时操作:垃圾回收

三、线程编程

3.1 线程的创建方式

  • 继承Thread类:

    • 重写run方法
  • 实现Runnable接口:

    • 重写run方法
    • 将Runnable交给Thread执行
  • Callable接口和FutureTask

    • 基于委托的适配器模式(FutureTask适配Runnable和Callable)
    • 将FutureTask(Runnable)交给Thread执行
  • 线程池

    • 将Runnable交给线程池的execute方法执行
public class ThreadTest {
    public static void main(String[] args) {
        //方式1 Thread
        MyThread myThread = new MyThread("w");
        myThread.start();
        //方式2 Runnable
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable,"我的线程2").start();
        //方式3 Callable和FutureTask
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
        new Thread(futureTask,"我的线程3").start();
        //方式4 线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 2L, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        threadPool.execute(myRunnable);
    }
}

class MyThread extends Thread {
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println("我的线程");
    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("创建可执行任务");
    }
}
class MyCallable implements Callable {
    @Override
    public Integer call() throws Exception {
        System.out.println("创建MyCallable");
        return 1;
    }
}

3.2 线程优先级

Java线程优先级是整数,范围(Thread.MIN_PRIORITY )1 —(Thread.MAX_PRIORITY )10,默认优先级 NORM_PRIORITY(5)。

更改优先级

  • setPriority(int newPriority)

3.3 关键字和方法讲解

关键字

  • volatile:变量可见,防止指令重排
  • synchronzied:同步悲观锁
    • 修饰普通方法:锁当前对象
    • 修饰静态方法:锁类对象
    • 修饰同步方法块:锁synchronized括号里配置的对象

方法

  • wait:
    • 是对象进入等待态,同步阻塞
    • wait会释放锁,出现中须定义同步锁
  • notify:随机唤醒等待队列中持有相同锁的对象
  • notifyAll:唤醒所有等待队列中持有相同锁的对象
  • sleep:线程休眠
  • join:让调用方法的线程加入到当前线程,顺序执行
  • yield:让步,让相同或更高优先级的线程执行
  • interrupt:设置线程中断标志位为true
    • interrupt可以的打断sleep,wait,join使其抛出异常
  • interrupted:返回中断状态,并清除中断状态
  • isInterrupted:返回中断状态,不清除中断状态

问题:

volatile原理:

将变量的修改直接写入主存中,保证各个线程对于变量的可见性。

sleep方法和wait方法:

  • sleep是Thread类的静态方法,wait是Object的实例方法
  • sleep不会释放锁,wait会释放锁
  • wait用于同步代码块,sleep不需要写在同步代码块中interrupt唤醒
  • wait作用与对象,sleep作用于线程

为什么wait方法是Object类

  • 简单来说,Java中锁的级别是对象,由对象本身(共享资源本身)来持有锁。

3.3 线程停止方式

  • 运行完停止

  • 自定义中断标志符

    使用volatile修饰一个变量作为标识

    volatile boolean flag = false;
    
    Thread t = new Thread(()->{
       while (true) {
           System.out.println("hello");
           if (flag) {
               System.out.println("线程结束");
               break;
           }
       }
    });
    t.start();
    Thread.sleep(1000);
    flag = true;
    
  • interrupt

    使用interrupt和isInterrupted使线程停止。

    Thread t = new Thread(()->{
       while (!Thread.currentThread().isInterrupted()) {
           System.out.println("hello");
       }
    });
    t.start();
    Thread.sleep(1000);
    t.interrupt();
    

3.4 多线程编程步骤

  • 创建资源类,在资源类创建属性和操作方法
  • 在资源类操作方法
    • 判断
    • 干活
    • 通知
  • 创建多个线程,调用资源类的操作方法
  • 防止虚假唤醒

3.5 生产者消费者模式

public class ConsumerProducer {
    public static void main(String[] args) {
        Buffer buffer = new Buffer(5);
        Producer producer = new Producer(buffer);
        Consumer consumer = new Consumer(buffer);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}
//资源类
class Buffer {
    private Queue<Integer> queue;
    private int size;

    public Buffer(int size) {
        this.queue = new LinkedList<Integer>();
        this.size = size;
    }

    public synchronized void add(int i) throws InterruptedException {
        //判断
        while (queue.size() >= size) {
            wait();
        }
        //干活
        queue.add(i);
        System.out.println("生产者生成了:"+i);
        //通知
        notifyAll();
    }

    public synchronized int get() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }
        Integer value = queue.poll();
        notifyAll();
        return value;
    }
}

class Producer implements Runnable {
    private Buffer buffer;
    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                buffer.add(i);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    private Buffer buffer;

    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                int thing = buffer.get();
                System.out.println("消费者消费了:"+thing);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

JUC并发编程

一、JUC简介

1、什么是JUC:

  • java.util.concurrent工具包的简称
  • 这是一个处理线程的工具包,JDK1.5出现的

2、线程Thread的状态

  • NEW:新建
  • RUNNABLE:准备就绪
  • BLOCKED:阻塞
  • WATING:等待
  • TIMED_WAITING:过时不候
  • TERMINATED:终止

3、管程

  • 监视器Monitor
  • 通常说的
  • 是一种同步机制,保证同一时间,只有一个线程访问被保护的数据或者代码
  • JVM的同步基于进入和退出,使用管程对象实现

4、用户线程和守护线程

  • 用户线程:主线程结束,用户线程还在运行,jvm存活
  • 守护线程:没有用户线程,都是都是守护线程,vnm结束

二、Lock接口

Lock和synchronize的比较:

  • Lock锁提供了比同步方法和语句更广泛的操作。结构更灵活,并且可以支持多个关联的条件对象(Condition)
  • Lock是接口,不是java内置的,synchronize是java的关键字。
  • Lock需要手动释放锁,synchronize不用手动释放锁,发生异常时,synchronize会自动释放锁,Lock不可以。
  • Lock等待的线程可以使用interrupt中断,synchronize等待的线程不会响应中断
  • 通过Lock可以知道是否成功获得锁(tryLock方法),synchronize不行

synchronized卖票例子

class Ticket {
    private int ticketNum = 30;

    public synchronized void sale() {
        if( ticketNum > 0) {
            System.out.println(Thread.currentThread().getName()+"卖出第"+(ticketNum--)+"票,还剩"+ticketNum+"张票");
        }
    }
}
public class SaleTicketSyn {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0; i< 30; i++)
                    ticket.sale();
            }
        },"A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0; i< 30; i++)
                    ticket.sale();
            }
        },"B").start();
    }
}

可重入锁卖票

class TicketL {
    private int ticketNum = 30;
    private final ReentrantLock lock = new ReentrantLock();

    public synchronized void sale() {
        lock.lock();
        try {
            if( ticketNum > 0) {
                System.out.println(Thread.currentThread().getName()+"卖出第"+(ticketNum--)+"票,还剩"+ticketNum+"张票");
            }
        } finally {
            lock.unlock();
        }
    }
}

三、虚假唤醒

虚假唤醒问题:

  • 出现情况:使用if条件判断,可能会出现虚假唤醒问题
    • 当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功
  • 出现原因:
    • 因为if只会执行一次,执行完会接着向下执行if()外边的,可能出现有线程跳过if条件的情况
    • 而while不会,直到条件满足才会向下执行while()外边的

出现虚假唤醒的例子

class Share {
    private int num = 0;

    public synchronized void incr() throws InterruptedException {
        if (num != 0) {  //使用if
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+":"+num);
        this.notifyAll();
    }
    public synchronized void decr() throws InterruptedException {
        if (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+":"+num);
        this.notifyAll();
    }
}

public class AddMinusSyn {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i=0; i<20; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i=0; i<20; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}

防止虚假唤醒的例子:

class ShareL {
    private int num = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void incr() throws InterruptedException {
        lock.lock();
        try {
            while (num != 0) {  //使用while
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+":"+num);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public void decr() throws InterruptedException {
        lock.lock();
        try {
            while (num != 1) {
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+":"+num);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

}

四、线程定制化通信

4.1 Condition条件队列

  • condition理解为条件(条件队列或条件变量),可以控制一组线程
  • condition和锁绑定

Object的wait和notify对比Condition的await和signal:

  • Object的监视是java底层实现的,Condition是语言层面实现的
  • Object只有一个等待队列,Condition支持多个
  • Conditon支持进入等待状态不响应中断,Object不支持
  • Conditon支持等待状态设置时间,Object不支持

4.2 实现方式

**思路:**使用标志位来控制线程执行顺序

例子:A,B,C轮替执行

class Share {
    private int flag = 1;
    private ReentrantLock lock = new ReentrantLock();
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    public void funA() throws InterruptedException {
        lock.lock();
        try {
            while (flag != 1) {
                c1.await();
            }
            this.flag = 2;
            System.out.println(Thread.currentThread().getName()+"funA");
            c2.signal();
        } finally {
            lock.unlock();
        }
    }
    public void funB() throws InterruptedException {
        lock.lock();
        try {
            while (flag != 2) {
                c2.await();
            }
            this.flag = 3;
            System.out.println(Thread.currentThread().getName()+"funB");
            c3.signal();
        } finally {
            lock.unlock();
        }
    }
    public void funC() throws InterruptedException {
        lock.lock();
        try {
            while (flag != 3) {
                c3.await();
            }
            this.flag = 1;
            System.out.println(Thread.currentThread().getName()+"funC");
            c1.signal();
        } finally {
            lock.unlock();
        }
    }
}

五、集合的线程安全

异常:并发修改异常

列表的线程不安全

解决方案:

  • Vector:效率低,比较老

  • Collections:

    • Collections.synchronziedList(new ArrayList<>()) 会返回一个线程安全的集合
    • 比较老
  • CopyOnWriteArrayList:写时复制

    • 原理:写入数据的时候,将原来的数组复制一份,在复制的那个数组加锁写入数据,之后覆盖掉原来的数组
    • 读不加锁,写加锁

HashSet线程不安全

解决方案:

  • CopyOnWriteHashSet

HashMap线程不安全

解决方案:

  • ConcurrentHashMap

六、多线程锁

6.1 死锁

两个及以上进程在执行过程中,由于竞争资源或彼此通信而造成的一种阻塞现象,若无外力作用,它们将永远等待下去。

产生死锁的四个原因

  • 资源独占
  • 不可抢占
  • 保持请求
  • 循环等待

判断死锁

  • jps -l:查看进程
  • jstack 进程:可以查看是否死锁

6.2 公平锁和非公平锁

  • 非公平锁:
    • 多个线程去获取锁的时候会直接获取,获取不到会进入队列排队
    • 减少CPU唤醒线程的开销,效率更高
  • 公平锁:
    • 多个线程按照申请锁的顺序去获得锁,线程会进入队列排队,只有第一位才能得到锁
    • 不会出现线程饿死

6.3 可重入锁和不可重入锁

  • 可重入锁:线程获取了该对象的锁后,可以获取该对象的其他锁,将锁的次数加一
    • 避免死锁
  • 不可重入锁:线程获取了该对象的锁后,获取该对象的其他锁时,将判断该对象的锁是否释放,未释放就不能获取

例子

  • 执行methodA方法
    • 可重入锁在获得methodA的锁后,因为是同一线程,也可以获取methodB的锁
    • 不可重入锁在获得methodA的锁后,执行methodB,但是B方法已上锁,不能需要等待,发生死锁
public class LockTest {
    Lock lock = new Lock();
    
    public void methodA() {
        lock.lock();
        try {
          	System.out.println("A方法已上锁");  
            this.methodB();
        } finally {
            lock.unlock();
        }
    }
    public void methodB() {
        lock.lock();
        try {
          	System.out.println("B方法已上锁");  
        } finally {
            lock.unlock();
        }
    }
}

6.4 乐观锁和悲观锁

  • 乐观锁
    • 简介:在操作数据时比较乐观,认为没有其他线程同时操作数据,不会上锁,但是在更新数据时会判断该数据是否被修改过。
    • 实现:
      • CAS机制
      • 版本号比较
    • 适合读操作较多的情况
  • 悲观锁
    • 简介:操作数据时比较悲观,认为别的线程也会同时修改数据,每次拿数据时都会上锁。
    • 实现:synchronized和Lock的可重入锁
    • 适合写操作较多的情况

6.5 读写锁

读写锁:一个资源可以被多个读线程访问,或者一个写线程访问,但不能同时存在读写线程,读写互斥,读读共享。

实现方法

  • 使用ReentrantReadWriteLock,可以获取读锁和写锁

6.6 锁降级

  • 获取写锁——获取读锁——释放写锁——释放读锁
  • 写锁可以降级为读锁,读锁不能升级为写锁

七、Callable接口

使用Callable接口可以时线程有返回值。

Runnable接口和Callable接口

  • Runnable接口无返回值,Callable接口有返回值
  • Runnable接口无法抛出异常,Callable接口可以抛出异常
  • 一个是run()方法,一个是call()方法

FutureTask:

  • 可取消的异步计算

原理

基于委托的适配器模式

多线程学习笔记_第2张图片

案例:

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //方式1
        FutureTask<Integer> futureTask = new FutureTask<>(new CallableClass());
        //方式2lambda表达式
        FutureTask futureTask1 = new FutureTask(()->{
            System.out.println(Thread.currentThread().getName()+"进入callable");
            return 200;
        });

        new Thread(futureTask,"A").start();

        System.out.println(futureTask.get());


    }

}

class CallableClass implements Callable {

    @Override
    public Object call() {
        System.out.println(Thread.currentThread().getName()+"进入callable");
        return 200;
    }
}

八、辅助类

8.1 减少计数CountDownLatch

CountDownLatch类可以设置一个计数器,然后通过countDown方法来进行减一的操作,使用await方法等待计数器不大于0,然后继续执行await方法之后的语句。

  • CountDownLatch主要有两个方法,一个或多个线程调用await方法时,这些线程会阻塞
  • 其他线程调用countDown方法会将计数器减一(调用该方法的线程不会阻塞)
  • 计数器减为0时,因await方法阻塞的线程会被唤醒,继续执行

使用流程:

        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i=0; i<6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"走了");
                countDownLatch.countDown();
            },i+"").start();
        }
        
        countDownLatch.await();
        System.out.println("线程都运行完了");

8.2 循环栅栏CyclicBarrier

CyclicBarrier构造方法第一个参数时目标障碍数,每执行一次障碍数加一,达到目标障碍数之后才会执行await之后的语句。可以理解为加一操作。

使用流程

final int NUM = 7;

CyclicBarrier cyclicBarrier = new CyclicBarrier(NUM,()->{
    System.out.println("阻塞七个,我可以运行了");
});

for (int i = 0; i < 7; i++) {
    new Thread(()->{
        System.out.println(Thread.currentThread().getName()+"阻塞");
        try {
            cyclicBarrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    },i+"").start();
}

CyclicBarrier和CountDownLatch的区别

  • 动作的实施者:CountDownLatch计数为0时,动作实施者时主线程,而CyclicBarrier是其他线程
  • 使用的次数:CountDownLatch只能使用一次,CyclicBarrier可以使用reset()方法重复使用

8.3 信号灯Semaphore

六辆车停3个停车位,加减的操作。

Semaphore semaphore = new Semaphore(3);

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 (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }
    },String.valueOf(i)).start();
}

十、阻塞队列

10.1 概述

多线程学习笔记_第3张图片

10.2 常见的BlockingQueue

ArrayBlockingQueue

由数组结构组成的有界阻塞队列

LinkedBlockingQueue

由链表组成的有界阻塞队列

DelayQueue

使用优先级队列实现的延迟无界阻塞队列

PriorityBlockingQueue

支持优先级排序的无界阻塞队列

SynchronousQueue

单个元素的队列

LinkedTransferQueue

链表组成的无界阻塞队列

LinkedBlockingDeque

链表组成的双向队列

10.3 核心方法

多线程学习笔记_第4张图片

十一、线程池

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

11.1 线程池的框架

Java中的线程池时通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个类

多线程学习笔记_第5张图片

11.2 线程池的使用方式

newFixedThreadPool

特征:

  • 一池n线程
  • 线程可以重复被使用,在显示关闭之前,都将一直存在
  • 超出一定量的线程被提交时候需要在队列中等待

newSingleThreadExecutor

  • 一池一线程

newCachedThreadPool

  • 线程池根据需求创建,可扩容

使用演示:

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5);
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
        ExecutorService threadPool3 = Executors.newCachedThreadPool();

        try {
            for (int i = 0; i < 10; i++) {
                threadPool1.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "办理业务");
                });
            }
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
            threadPool1.shutdown();
        }
    }
}

11.3 线程池参数讲解

  • int corePoolSize:常驻线程数量
  • int maximumPoolSize:最大线程数量
  • long keepAliveTime:线程存活时间,(超出常驻线程数量的线程,存活的时间)
  • TimeUnit unit:时间单位
  • BlockingQueue< Runnnable> workQueue:阻塞队列
  • ThreadFactory threadFactory:线程工程
  • RejectedExecutionHandler handler:拒绝策略

11.4 拒绝策略

  • CallerRunsPolicy:谁提交的交给谁执行
  • AbortPolicy:抛异常
  • DiscardPolicy:直接丢弃
  • DiscardOldestPolicy:丢弃队列中时间最长的

11.5 自定义线程池

实际使用中一般都是自定义线程池:

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 2L, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(3),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());

十二、Fork/Join框架

12.1 Fork/Join框架简介

Fork/Join可以将一个大的任务拆分成许多小任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。

  • fork进行拆分
  • join进行合并

12.2 使用案例

计算a加到b的值,每10个进行计算

class MyTask extends RecursiveTask<Integer> {
    private static final Integer 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() {
        if((end - begin) <= VALUE) {
            for (int i = begin; i <= end; i++) {
                result = result + i;
            }
        } else {
            int middle = (begin + end)/2;
            MyTask myTask1 = new MyTask(begin,middle);
            MyTask myTask2 = new MyTask(middle + 1,end);
            myTask1.fork();
            myTask2.fork();
            result = myTask1.join() + myTask2.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);
        Integer res = forkJoinTask.get();
        System.out.println(res);
        forkJoinPool.shutdown();
    }
}

十三、CompletableFuture

CompletableFuture异步回调的使用

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

        //没有返回值
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
            System.out.println("hi");
        });
        completableFuture.get();

        //有返回值
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(()->{
            System.out.println("hi");
            return 43;
        });

        Integer integer = completableFuture1.whenComplete((t, u) -> {
            System.out.println(t + " 返回值");
            System.out.println(u + " 异常");
        }).get();

        System.out.println("返回值"+integer);
        
    }
}

你可能感兴趣的:(java)