---
url: /notes/java-se/线程/index.md
---
# 线程

**并发** ：指两个或者多个事件在**同一个时间段** 内发生

**并行** ：之两个或者多个事件在**同一时刻** 发生

**程序** ：可执行的文件

**进程** ：一个内存中运行的应用程序，每个进程都有自己独立的内存空间，一个应用程序可以运行多个进程

进程也是程序的一次执行过程，是系统运行的基本单位，系统运行程序即是一个进程从创建到消亡的过程

**线程** ：是进程中的一个最小执行单元，负责当前进程的执行，一个进程中至少有一个线程，一个进程中可以有多个线程，当一个程序有多个线程执行时，称为这个程序为多线程程序

简而言之：一个程序运行后至少有一个进程，一个进程可以包含多个线程

# 线程的调度

1. **分时调度** ：所有的线程轮流使用CPU的使用权，平均分配每个线程的占用CPU的时间

2. **抢占式调度** ：优先让优先级比较高的线程来使用CPU，如果线程的优先级相同，那么会随机选择一个，java使用的就是抢占式调度

# 创建线程

### 继承Thread类

Thread类是描述线程的类，我们想要实现多线程，就必须要继承它

#### 实现步骤

1. 创建一个类继承`Thread`

2. 重写`Run`方法，设置线程任务

3. 创建一个`Thread`**子类对象**

4. 调用`start()`开启多线程

```java
//子线程
public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(111);
    }
}
public class Demo {
    //主线程
    public static void main(String[] args) {
        Thread thread=new MyThread();
        thread.start();
        for (int i = 0; i < 50; i++) {
            System.out.println(222);
        }
    }
}
```

#### 常用方法

`public Thread() `分配一个**新的** `Thread`**对象**
`public final String getName()` 返回此**线程的名称** 。
`public final void setName(String name) `将此线程的**名称更改** 为等于参数`name`
`public static void sleep(long millis) `使当前**正在执行的线程停留** （暂停执行）指定的毫秒数
`public static Thread currentThread()` 返回对当前**正在执行的线程对象** 的**引用** 。

```java
public class MyThread extends Thread{
    @Override
    public void run() {
        try {
            Thread.currentThread().sleep(2000);
            Thread.currentThread().setName("子线程1");
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
public class DemoThread {
    public static void main(String[] args) {
        MyThread t=new MyThread();
        t.start();
        Thread.currentThread().setName("主线程main");
        System.out.println(Thread.currentThread().getName());
    }
}
```

![image-20211124225737679](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124225737679.png)

### 实现Runnable接口

Runnable接口：应该由那些打算通过某一线程执行其实例的类来实现。实现了接口后需要重写run方法

#### 实现步骤

1. 创建一个类，实现`Runnable`接口

2. 在实现类中重写`run`方法，并设置线程任务

3. **创建实现类对象**

4. `创建Thread类对象`，构造方法中传递实现类对象

5. 调用Thread类中的`start()`，开启多线程

```java
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
public class DemoRunnable {
    public static void main(String[] args) {
        MyRunnable mr=new MyRunnable();
        Thread thread=new Thread(mr);
        thread.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
```

![image-20211124225746604](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124225746604.png)

#### 实现Runnable接口的优点

1. 避免了单继承的局限性；

    1. 一个类只能继承一个类，如果这个类继承Thread就不能再继承其他的类

    2. 实现了Runnable接口，可还可以继承其他的类和接口

2. 增强程序的扩展性，降低程序的耦合度

    1. 实现接口，把设置线程任务和开启线程任务进行了分离

    2. 实现类中，重写了run方法：用来设置线程任务

    3. 创建Thread类对象，调用start方法：开启多线程

#### 使用匿名内部类创建多线程

```java
public class InnerClassRunnable {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getId() + ":" + i);
                }
            }
        });
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getId() + ":" + i);
        }
    }
}
```

### 实现 Callable接口

### 创建线程池

> 下面线程池讲解

# 线程安全

如果由多个线程在同时运行，而且这些线程可能会同时运行这段代码，那么程序每次运行结果和单线程每次运行的结果是一样的，且其他的变量的值和预期是一样的，这就是线程安全的

## 出现线程安全的原因

使用了多线程访问同一个资源，而且多个线程对同一个资源进行了操作

## 解决线程安全

需要使用**同步代码块** （`synchronized`）

### （1）同步代码块

#### 格式

```java
synchronized(锁对象){
  可能发生安全问题的代码
}
```

#### 注意

1. 通过代码中的锁对象，可以使用**任意的对象**

2. 但是必须要**保证多个线程使用的锁对象是同一个**

3. 锁对象的作用：把同步代码块加锁，只让一个线程在同步代码块中执行

```java
public class Demo1 {
    static int ticketCount=50;
    static Object obj=new Object();
    public static void main(String[] args) {

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    synchronized (obj){
                        if (Demo1.ticketCount<=0){
                            System.out.println("窗口1票已售完！");
                            return;
                        }else{
                            System.out.println("窗口1正在卖第"+Demo1.ticketCount--+"张票");
                        }
                    }
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    synchronized (obj){
                        if (Demo1.ticketCount<=0){
                            System.out.println("窗口2票已售完！");
                            return;
                        }else {
                            System.out.println("窗口2正在卖第"+Demo1.ticketCount--+"张票");
                        }
                    }
                }
            }
        });
        Thread t3=new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    synchronized (obj){
                        if (Demo1.ticketCount<=0){
                            System.out.println("窗口3票已售完！");
                            return;
                        }else{
                            System.out.println("窗口3正在卖第"+Demo1.ticketCount--+"张票");
                        }
                    }
                }
            }
        });
        t1.start();
        t2.start();
        t3.start();
    }
}

```

![image-20211124225922204](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124225922204.png)

### （2）同步方法

```java
修饰词 synchronized 返回值 方法名称(参数){}
```

此时锁住的是对象本身，调用者自己本身

### （3）锁机制

`public interface Lock`: Lock实现提供比使用synchronized方法个语句可以获得更广泛的锁定操作
`ReentrantLock`
Lock锁也称为**同步锁** ：
`void lock() `**上锁**
`void unlock` **释放锁**

```java
ReentrantLock lock=new ReentrantLock();
lock.lock();
lock.unlock();
```

```java
public class Demo {
    static int sum=0;
    static Object obj=new Object();
    public static void main(String[] args) {
        ReentrantLock lock=new ReentrantLock();
        lock.lock();
        new Thread(()->{Demo.sum++;}).start();
        new Thread(()->{Demo.sum++;}).start();
        new Thread(()->{Demo.sum--;}).start();
        new Thread(()->{Demo.sum--;}).start();
        lock.unlock();
        new Thread(new MyThread1()).start();
    }
}
```

# 锁池和等待池

## 锁池

假设线程A已经拥有了某个对象(注意:不是类)的锁，而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块)，由于这些线程在进入对象的synchronized方法之前必须先**获得该对象的锁的拥有权** ，但是**该对象的锁目前正被线程A拥有** ，所以这些线程就进入了该对象的锁池中。

## 等待池

假设一个线程A**调用** 了某个对象的**wait()方法** ，线程A就会**释放该对象的锁** 后，进入到了该对象的等待池中

<https://blog.csdn.net/weixin_42504145/article/details/85329386>

# 线程生命周期

![image-20211124225934945](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124225934945.png)

1. 当程序使用 **new 关键字** 创建了一个线程之后，该线程就处于新建状态，此时仅由 JVM 为其分配 内存，并初始化其成员变量的值

2. 当线程对象调用了 **start()方法** 之后，该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和 程序计数器，等待调度运行。

3. 如果处于就绪状态的线程获得了 CPU，**开始执行 run()方法的线程执行体** ，则该线程处于运行状态。

4. 阻塞状态是指**线程因为某种原因放弃了 cpu 使用权** ，也即让出了 cpu timeslice，暂时停止运行。 直到线程进入可运行(runnable)状态，才有机会再次获得 cpu timeslice 转到运行(running)状态。

![image-20211124225949281](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124225949281.png)

5. 线程会以三种方式结束，结束后就是死亡状态。

![image-20211124230004657](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124230004657.png)

`public final native void notify()`

线程**被唤醒**

`public final native void wait()`

线程调用该方法后线程会进入

**等待状态**

```java
public class WaittingDemo {
    public static void main(String[] args) {
        Object obj=new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    synchronized (obj){
                        System.out.println("老板来碗热干面");
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("开吃");
                        System.out.println("============");
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    synchronized (obj){
                        System.out.println("面做好了，准备开吃");
                        obj.notify();
                    }
                }
            }
        }).start();
    }
}
```

## sleep 与 wait 区别

1. 对于 `sleep()`方法，我们首先要知道该方法是属于 Thread 类\*\*中的。而 \*\* **wait()** **方法，则是属于** Object 类中的

2. sleep()方法导致了程序暂停执行指定的时间，让出 cpu 该其他线程，但是他的监控状态依然保持，当指定的时间到了又会自动恢复运行状态。

3. **在调用 sleep()方法的过程中，线程不会释放对象锁** 。

4. 而当**调用 wait()方法的时候，线程会放弃对象锁，进入等待此对象的等待锁定池** ，只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

## start 与 run 区别

1. **start（）方法来启动线程** ，真正实现了多线程运行。这时**无需等待 run 方法体代码执行完毕** ，可以直接继续执行下面的代码。

2. 通过调用 Thread 类的 start()方法来启动一个线程， 这时此线程是处于**就绪状态** ， 并没有运行。

3. 方法 run()称为线程体，它包含了要执行的这个线程的内容，线程就进入了**运行状态** ，**开始运行 run 函数当中的代码** 。 Run 方法运行结束， 此线程终止。然后 CPU 再调度其它线程。

# 线程通信

```java
/**
 * @Description 面条
 * @Classname Noddle
 * @Date 2021/8/13 9:21
 * @Created by 折腾的小飞
 */
public class Noddle {
    String name;
    String burdening;
    //状态：true有，false没有
    boolean flag = false;
}
/**
 * @Description 早餐的生产者、
 * 生产早餐
 * noodle
 * 判断是否已经有面条
 * 如果有则wait一段时间，表示卖面条
 * 如果没有则开始制作
 * <p>
 * 早餐店和面条之间的关系 互斥
 * @Classname Sell
 * @Date 2021/8/13 9:17
 * @Created by 折腾的小飞
 */
public class Sell extends Thread {
    private Noddle noddle;

    //通过构造方法设置面条值
    public Sell(Noddle noddle) {
        this.noddle = noddle;
    }

    @Override
    public void run() {
        //定义一个变量
        int count = 0;
        //生产面条
        while (true) {
            synchronized (noddle) {
                //对面条进行判断
                if (noddle.flag == true) {
//                     有就停止制作面，进行卖面条操作；
                    try {
                        noddle.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 被唤醒后，相当于开始生产面条
                if (count % 2 == 0) {
                    noddle.name="热干面";
                    noddle.burdening="花生酱+香菜";
                }else{
                    noddle.name="炸酱面";
                    noddle.burdening="葱";
                }
                count++;
                System.out.println("早餐店正在生产："+noddle.name+"，加的配料是："+noddle.burdening);

                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                noddle.flag=true;
                noddle.notify();
                System.out.println("我的"+noddle.name+"已经做好了，给你加的配料是："+noddle.burdening+"，可以来吃了！");
            }
        }
    }
}
/**
 * @Description 买方 买早餐
 * 买面条的
 * 需要判断有没有面条
 * 状态：false 进入等待期wait()
 * true 开始吃面条
 * 吃完了，把面条状态改为false
 * 再叫sell做面条
 * @Classname Buyer
 * @Date 2021/8/13 9:27
 * @Created by 折腾的小飞
 */
public class Buyer extends Thread {
    private Noddle noddle;

    public Buyer(Noddle noddle) {
        this.noddle = noddle;
    }

    @Override
    public void run() {
        //让客户一直来吃面条
        while (true) {
            synchronized (noddle) {
                if (noddle.flag == false) {
                    try {
                        noddle.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //唤醒后开始吃面条
                System.out.println("顾客正在享用早餐：【" + noddle.name + "】，加的配料是：【" + noddle.burdening + "】");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //吃完
                noddle.flag = false;
                //叫早餐店生产面条
                noddle.notify();
                System.out.println("老板我的面吃完了，再来一碗");
                System.out.println("========================");

            }
        }
    }
}
/**
 * @Description 开启多线程任务
 * @Classname Demo
 * @Date 2021/8/13 9:15
 * @Created by 折腾的小飞
 */
public class Demo {
    public static void main(String[] args) {
        Noddle noddle=new Noddle();
        new Sell(noddle).start();
        new Buyer(noddle).start();
    }
}
```

# 线程池

就是一个容纳多个线程的容器，其中的线程可以反复地利用，省去了我们频繁创建线程对象的操作，无需反复创建线程而消耗过多的资源；

为了节省资源的一个消耗

![image-20211124230029347](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124230029347.png)

Java 里面线程池的顶级接口是 `Executor`，但是严格意义上讲 Executor 并不是一个线程池，而 只是一个执行线程的工具。

真正的线程池接口是

`ExecutorService`

## `Executors`

JDK1.5以后提供了一个线程池工厂类：`public class Executors`

### `newFixedThreadPool`

`public static ExecutorService newFixedThreadPool(int nThreads)`
创建一个可重用固定线程数的线程池，以共享的无界队列方式来运行这些线程。

> 在任意点，在大多数 nThreads 线程会处于**处理任务的活动状态** 。
> 如果在所有线程处于活动状态时**提交附加任务** ， 则在有可用线程之前，附加任务**将在队列中等待** 。如果在关闭前的执行期间由于失败而导致任何 线程终止，那么一个新线程将代替它执行后续的任务（如果需要）。在某个线程被显式地关闭之 前，池中的线程将一直存在。

### `newCachedThreadPool`

`public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)`
创建一个可根据需要创建新线程的线程池，但是在以前构造的线程可用时将重用它们

> \*\*调用 \*\* **execute** **将重用以前构造 的线程（如果线程可用）。如果现有线程没有可用的，则创建一个新线程并添加到池中。终止并 从缓存中移除那些已有 60 秒钟未被使用的线程。对于执行 很多短期异步任务** 的程序而言，这些线程池通常可提高程序性能。
> 因此，长时间保持空闲的线程池不会使用任何资源。

### `newScheduledThreadPool`

`public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)`
创建一个线程池，可以调度命令在给定的延迟后运行，或者定期执行。

`ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)`

提交在给定延迟后启用的一次性任务。

> **参数**\
> `command` - 要执行的任务
> `delay` - 从现在起延迟执行的时间
> `unit` - 延迟参数的时间单位

**结果**\
表示任务未完成的ScheduledFuture，其完成后 `get()`方法将返回 `null`

**异常**\
`RejectedExecutionException` -  如果无法安排执行任务
`NullPointerException` -  如果命令或单位为空

`ScheduledFuture]<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)`
提交定期操作，该操作在给定的初始延迟后首先启用，随后在给定的时间段内启用; 也就是说，执行将在`initialDelay`之后开始，然后是`initialDelay +  period` ，然后是`initialDelay + 2 * period` ，依此类推。【固定时间间隔】

> \*\*参数 \*\*
> `command` - 要执行的任务
> `initialDelay` - 延迟首次执行的时间
> `period` - 连续执行之间的时间间隔
> `unit` - initialDelay和period参数的时间单位
> **结果**
> ScheduledFuture表示一系列重复任务的待完成。
> 未来的`get()`方法永远不会正常返回，并且会在任务取消或任务执行异常终止时抛出异常。

\*\*异常 \*\*
`RejectedExecutionException` -  如果无法安排任务执行
`NullPointerException` -  如果命令或单位为空
`IllegalArgumentException`  - 如果周期小于或等于零

`ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)`
提交在给定的初始延迟之后首先启用的定期动作，并且随后在一次执行的终止和下一次执行的开始之间给定延迟。

```java
public class DemoScheduedExecutorService {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledPool= Executors.newScheduledThreadPool(3);
        // 延时设定时间（initialDelay）后只执行一次任务。
        scheduledPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("延迟2秒");
            }
        },2, TimeUnit.SECONDS);
        //延时设定时间（initialDelay）后执行第一次任务，随后每次任务执行完后延迟设定时间（delay）再次执行任务。
        scheduledPool.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("延迟5秒后，重新设定间隔时间");
            }
        },5,3,TimeUnit.SECONDS);
        //延时设定时间（initialDelay）后执行第一次任务，每隔时间间隔（period）再次执行任务。
        scheduledPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("延迟1秒后每三秒执行一次");
            }
        },1,3,TimeUnit.SECONDS);
    }
}
```

### `newSingleThreadExecutor`

`public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)`
返回一个线程池（这个线程池**只有一个线程** ）,这个线程 池可以在线程死后（或发生异常时）重新启动一个线程来替代原来的线程继续执行下去！

## `ExecutorService`接口

`public interface ExecutorService`
用来从线程池中获取线程，调用start方法，并执行任务

`void shutdown()` 关闭线程池
启动有序关闭，其中先前提交的任务将被执行，但不会接受任何新任务。 如果已经关闭，调用没有其他影响。
此方法不会等待先前提交的任务完成执行。 使用`awaitTermination`来做到这一点。
​

`Future<?> submit(Runnable task)` 执行任务
提交Runnable任务以执行并返回表示该任务的Future。 \_成功\_完成后，Future的`get`方法将返回`null`

1. **有返回值** 的任务必须实现 `Callable 接口`

2. **无返回值** 的任务必须 `Runnable 接口`

执行 Callable 任务后，可以\*\*获取一个 \*\* **Future** **的对象** ，在该对象上调用 `get` 就可以获取到 Callable 任务 返回的 Object 了，再结合线程池接口 ExecutorService 就可以实现传说中有返回结果的多线程`V get()`
如果需要等待计算完成，然后检索其结果。

```java
public class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        return Thread.currentThread();
    }
}
public class DemoFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建一个线程池。里面有2个线程；
        // 此方法只要不主动销毁线程，就算中断，也会创建一个新线程接替
        ExecutorService pool= Executors.newFixedThreadPool(3);
        //创建多个有返回值的任务
        List<Future> list=new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            Callable c=new MyCallable();
            //执行任务并获取Future对象
            Future f=pool.submit(c);
            list.add(f);
        }
        //关闭线程池
        pool.shutdown();
        //获取所有并发任务的运行结果
        for (Future future : list) {
            //从Future对象上获取任务的返回值，并输出到控制台
            System.out.println(future.get().toString());
        }
    }
}
```

```java
public class MyCallable implements Callable<Boolean> {
    private Integer age;

    public MyCallable(Integer age) {
        this.age = age;
    }

    @Override
    public Boolean call() throws Exception {
        if (age>20){
            return true;
        }else{
            return false;
        }
    }
}
//1.创建一个线程池，线程池中有多少个原始线程
ExecutorService pool= Executors.newFixedThreadPool(3);
Future<Boolean> submit1 = pool.submit(new MyCallable(19));
Future<Boolean> submit2 = pool.submit(new MyCallable(23));
System.out.println(submit1.get().toString());
System.out.println(submit2.get());
//关闭线程池
pool.shutdown();
```

## 线程池的使用步骤

1. 使用工厂类`Executors`里面提供的**静态方法生产指定数量的线程池**

2. 创建一个类，实现Runnable接口，重写run方法，设置线程任务

3. 调用`ExceutorService`**接口** 里的`submit方法`，传递线程任务，开启线程执行run方法

4. 调用`ExceutorService`**接口** 里的`shutdown方法`销毁线程池（不建议使用）

```java
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
}
//1.创建一个线程池，线程池中有多少个原始线程
ExecutorService pool= Executors.newFixedThreadPool(3);
//调用submit方法
pool.submit(new MyRunnable());
//关闭线程池
pool.shutdown();
```

## 线程池的组成

1. **线程池管理器** ：用于创建并管理线程池

2. **工作线程** ：线程池中的线程

3. **任务接口** ：每个任务必须实现的接口，用于工作线程调度其运行

4. **任务队列** ：用于存放待处理的任务，提供一种缓冲机制

Java 中的线程池是通过 Executor 框架实现的，该框架中用到了 Executor，Executors， ExecutorService，ThreadPoolExecutor ，Callable 和 Future、FutureTask 这几个类。

![image-20211124230045892](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124230045892.png)

### ThreadPoolExecutor 的构造方法

```java
public ThreadPoolExecutor(int corePoolSize, 
                          int maximumPoolSize, 
                          long keepAliveTime, 
                          TimeUnit unit, 
                          BlockingQueue<Runnable> workQueue, 
                          ThreadFactory threadFactory, 
                          RejectedExecutionHandler handler);
1. corePoolSize：指定了线程池中的线程数量。
2. maximumPoolSize：指定了线程池中的最大线程数量。
3. keepAliveTime：当前线程池数量超过 corePoolSize 时，多余的空闲线程的存活时间，即多次时间内会被销毁。
4. unit：keepAliveTime 的单位。
5. workQueue：任务队列，被提交但尚未被执行的任务。
6. threadFactory：线程工厂，用于创建线程，一般用默认的即可。
7. handler：拒绝策略，当任务太多来不及处理，如何拒绝任务。

JDK 内置的拒绝策略如下： new ThreadPoolExecutor XXXXXXXX()
1. AbortPolicy ： 直接抛出异常，阻止系统正常运行。
2. CallerRunsPolicy ： 只要线程池未关闭，该策略直接在调用者线程中，运行当前被丢弃的任务。显然这样做不会真的丢弃任务，但是，任务提交线程的性能极有可能会急剧下降。
3. DiscardOldestPolicy ： 丢弃最老的一个请求，也就是即将被执行的一个任务，并尝试再次提交当前任务。
4. DiscardPolicy ： 该策略默默地丢弃无法处理的任务，不予任何处理。如果允许任务丢失，这是最好的一种方案。
以上内置拒绝策略均实现了 RejectedExecutionHandler 接口，若以上策略仍无法满足实际需要，完全可以自己扩展 RejectedExecutionHandler 接口。
```

```java
public class DemoThreadPoolExecutor {
 public static void main(String[] args) {

  ThreadPoolExecutor executor=new ThreadPoolExecutor(2, // 线程数量
                                                     5, // 最大线程数量
                                                     30, 
                                                     TimeUnit.SECONDS,
                                                     new ArrayBlockingQueue<Runnable(5),
                                                     Executors.defaultThreadFactory(),     
                                               new ThreadPoolExecutor.DiscardPolicy());

        for (int i = 0; i < 16; i++) {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }

        System.out.println("线程池中的线程数目："+executor.getPoolSize()+"，队列中等待执行的任务数目："+executor.getQueue()+"，已经执行完的线程数目："+executor.getCompletedTaskCount());
        executor.shutdown();
    }
}
```

![image-20211124230057575](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124230057575.png)

## Java 线程池工作过程

1. 线程池刚创建时，里面没有一个线程。任务队列是作为参数传进来的。不过，就算队列里面有任务，线程池也不会马上执行它们。

2. 当调用 execute() 方法添加一个任务时，线程池会做如下判断：a) 如果正在运行的线程数量小于 corePoolSize，那么马上创建线程运行这个任务；b) 如果正在运行的线程数量大于或等于 corePoolSize，那么将这个任务放入队列；c) 如果这时候队列满了，而且正在运行的线程数量小于 maximumPoolSize，那么还是要创建非核心线程立刻运行这个任务；d) 如果队列满了，而且正在运行的线程数量大于或等于 maximumPoolSize，那么线程池会抛出异常 RejectExecutionException。

3. 当一个线程完成任务时，它会从队列中取下一个任务来执行。

4. 当一个线程无事可做，超过一定的时间（keepAliveTime）时，线程池会判断，如果当前运
   行的线程数大于 corePoolSize，那么这个线程就被停掉。所以线程池的所有任务完成后，它
   最终会收缩到 corePoolSize 的大小。

![image-20211124230126302](https://note-1259190304.cos.ap-chengdu.myqcloud.com/note/image-20211124230126302.png)

# Lambada表达式

JDK1.8新特性

## 格式

由三部分组成

1. 一些参数

2. 一个箭头

3. 一段代码

```java
(参数) -> {代码}
()：接口（有且只有一个方法）中的抽象方法的参数列表；如果没有参数就空着，如果有多个参数，用逗号隔开
->：传递，把参数传递给方法体
{}：重写方法的方法体，方法体里面只有一行代码，{}可省略
```

```java
new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(222);
            }
        }).start();

        //使用lambada
        new Thread(
                ()->{
                    System.out.println(333);
                }
        ).start();

        //继续简化
        new Thread(() -> System.out.println(333)).start();
```

```java
public interface Person {
    void good(Integer age,String name);
}
public static void study(Person person){}
public static void main(String[] args) {
  //函数式编程
    study((Integer age,String name)->{
        System.out.println(age);
        System.out.println(name);
    });
}
```

# CompletableFuture

supplyAsync 有返回结果，runAsync没有返回结果

**`allOf()` 方法会等到所有的 `CompletableFuture` 都运行完成之后再返回**

`thenCompose()` 可以链接两个 `CompletableFuture` 对象，并将前一个任务的返回结果作为下一个任务的参数，它们之间存在着先后顺序。

`thenCombine()` 会在两个任务都执行完成后，把两个任务的结果合并。两个任务是并行执行的，它们之间并没有先后依赖顺序

通过 `handle()` 方法来处理任务执行过程中可能出现的抛出异常的情况

[CompletableFuture 详解 | JavaGuide](https://javaguide.cn/java/concurrent/completablefuture-intro.html#future-介绍)
并行处理
（1）单个处理返回

```
//异步执行两个任务
CompletableFuture<User> supplyAsync = CompletableFuture.supplyAsync(() -> {
            return getUser("汪浩天");
        }, threadPoolExecutor);
        supplyAsync.thenAccept(user -> {
            log.info("task1 执行结果 {}", user.getUserName());
        });
```

（2）一起返回处理

```java
//等待三个任务都完成，才执行
CompletableFuture<Void> taskUserAccountFuture = CompletableFuture.runAsync(() -> {
            fillUserAccount();
        }, threadPoolExecutor);

        CompletableFuture<Void> taskMpOpenIdFuture = CompletableFuture.runAsync(() -> {
            int i = 1 / 0;
            fillMpOpenId();
        }, threadPoolExecutor);

        CompletableFuture<Void> taskUnionIdFuture = CompletableFuture.runAsync(() -> {
            fillUnionId();
        }, threadPoolExecutor);

        CompletableFuture<Void> allFuture = CompletableFuture.allOf(taskUserAccountFuture, taskMpOpenIdFuture, taskUnionIdFuture);

        allFuture.join();

        log.info("{queryData} 任务执行完成!");
```

```java
//task2等待task1执行完成后，再执行
private static void task3() {
        try {
            // 创建第一个任务，直接返回 User 对象
            CompletableFuture<User> future = CompletableFuture.supplyAsync(() ->
                            {
                                int i = 1 / 0;
                                return getUser("汪浩天");
                            }
                            , threadPoolExecutor)
                    .handle((user, throwable) -> {
                        if (throwable != null) {
                            log.error("task1 执行异常", throwable);
                            // 可以返回默认值或者抛出自定义异常
                            throw new BusinessException(ErrorCode.SYSTEM_ERROR, throwable.getMessage());
                        }
                        log.info("task1 执行结果 {}", user.getUserName());
                        return user;
                    });

            // 等待第一个任务完成后执行第二个任务
            future.thenCompose(firstResult ->
                    CompletableFuture.supplyAsync(() ->
                                            getUser("涂鏊飞")
                                    , threadPoolExecutor)
                            .handle((user, throwable) -> {
                                if (throwable != null) {
                                    log.error("task2 执行异常", throwable);
                                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, throwable.getMessage());
                                }
                                log.info("task2 执行结果 {}", user.getUserName());
                                return user;
                            })
            ).join(); // 等待整个任务链完成
        } catch (Exception e) {
            log.error(e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, e.getMessage());
        }
    }
```
