十一、 JDK线程池

一、JDK线程池

1. 类图

在这里插入图片描述

2. 线程状态

  • ThreadPoolExecutor 使用int的高3位来表示线程池状态,低29位表示线程数量

在这里插入图片描述

二、ThreadPoolExecutor

1. 构造方法

int corePoolSize:                     核心线程数
int maximumPoolSize:                 最大线程数
long keepAliveTime:                  救急线程数执行任务完后存活时间
TimeUnit unit:                       救急线程数执行任务完后存活时间
BlockingQueue<Runnable> workQueue:   阻塞队列
ThreadFactory threadFactory:         线程生产工厂,为线程起名字
RejectedExecutionHandler handler:    拒绝策略 
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;
}

2. 核心线程和救急线程

1. 核心线程: 执行完任务后,会继续保留在线程池中

2. 救急线程:如果阻塞队列已满,并且没有空余的核心线程。那么会创建救急线程来执行任务
  2.1 任务执行完毕后,这个线程就会被销毁(临时工)
  2.2 必须是有界队列,如果是无界队列,则不会创建救急线程

3. 有界队列,核心线程满负荷,队列已满,无空余救急线程,才会执行拒绝策略

3. JDK拒绝策略

  • 如果线程达到最大线程数,救急线程也满负荷,且有界队列也满了,JDK 提供了4种拒绝策略
AbortPolicy:           调用者抛出RejectedExecutionException,  默认策略
CallerRunsPolicy:      调用者运行任务
DiscardPolicy:         放弃本次任务
DiscardOldestPolicy:   放弃队列中最早的任务,本任务取而代之

在这里插入图片描述

三、Executors工厂方法

  • 默认的构造方法来创建线程池,参数过多,JDK提供了工厂方法,来创建线程池

1. 固定大小

  • 核心线程数 = 最大线程数,救急线程数为0
  • 阻塞队列:无界
# 应用场景
任务量已知,但是线程执行时间较长
执行任务后,线程并不会结束
// 默认工厂
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
package com.dreamer.multithread.day09;

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

public class Demo01 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);

        /** 执行任务
         * 
         * pool-1-thread-2running
         * pool-1-thread-1running
         * pool-1-thread-1running
         */
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + "running"));
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + "running"));
        pool.execute(() -> System.out.println(Thread.currentThread().getName() + "running"));
    }
}

2. 带缓冲线程池

  • 核心线程数为0, 最大线程数为Integer的无限大
  • 全部是救急线程,等待时间是60s,60s后就会消亡
  • SynchronousQueue: 容量为0,只能存放一个,进来一个,必须出去后,后续的才能进来
  • 整个线程池数会随着任务数目增长,1分钟后没有其他活动会消亡
# 应用场景
1. 时间较短的线程
2. 数量大,任务执行时间长,会造成  OutOfMmeory问题
 public static ExecutorService newCachedThreadPool() {
     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                   60L, TimeUnit.SECONDS,
                                   new SynchronousQueue<Runnable>());
 }

3. 单线程池

  • 线程池大小始终为1个,不能改变线程数
  • 相比自定义一个线程来执行,线程池可以保证前面任务的失败,不会影响到后续任务
# 1. 和自定义线程的区别
自定义线程:  执行多个任务时,一个出错,后续都能不能执行了
单线程池:    一个任务失败后,会结束出错线程。重新new一个线程来执行下面的任务

# 2. 执行顺序
单线程池: 可以保证所有任务都是串行

# 3. 和newFixedThreadPool的区别
newFixedThreadPool:  可以修改线程大小
newSingleThreadExecutor:   不可以修改
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
package com.dreamer.multithread.day09;

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

public class Demo03 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newSingleThreadExecutor();
        /*第一个任务执行失败后,后续任务会继续执行*/
        pool.execute(() -> {
            int i = 1 / 0;
            System.out.println(Thread.currentThread().getName() + " first running");
        });

        pool.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " second running");
        });

        pool.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " third running");
        });
    }
}

四、提交任务

  • ExecutorService接口

1. execute

void execute(Runnable command);

2. submit

Future<?> submit(Runnable task);

// 可以从 Future 对象中获取一些执行任务的最终结果
<T> Future<T> submit(Runnable task, T result);

<T> Future<T> submit(Callable<T> task);
package com.dreamer.multithread.day09;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService pool = Executors.newCachedThreadPool();
        // future的一些执行结果
        Future<?> result = pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " running");
            }
        });

        TimeUnit.SECONDS.sleep(2);

        System.out.println("is task done: " + result.isDone());
        System.out.println("is task cancelled:" + result.isCancelled());
    }
}
package com.dreamer.multithread.day09;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Demo2 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService pool = Executors.newCachedThreadPool();

        // 返回具体的结果
        Future<String> result = pool.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                System.out.println(Thread.currentThread().getName() + " running");
                return "success";
            }
        });
        TimeUnit.SECONDS.sleep(2);
        System.out.println("is task done: " + result.isDone());
        System.out.println("is task canceled: " + result.isCancelled());
        // 获取结果的时候,主线程会阻塞
        System.out.println("task result: " + result.get());
    }
}

3. invokeAll

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
    throws InterruptedException;
package com.dreamer.multithread.day09;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Demo2 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService pool = Executors.newCachedThreadPool();

        Collection<Callable<String>> tasks = new ArrayList();
        for (int i = 0; i < 10; i++) {
            int round = i;
            tasks.add(new Callable() {
                @Override
                public Object call() throws Exception {
                    System.out.println(Thread.currentThread().getName() + " running");
                    return "success:" + round;
                }
            });
        }

        List<Future<String>> results = pool.invokeAll(tasks);
        for (Future<String> result : results) {
            System.out.println(result.get());
        }
    }
}

4. invokeAny

  • 集合中之要有一个任务执行完毕,其他任务就不再执行
package com.dreamer.multithread.day04;

import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Test03 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService service = Executors.newFixedThreadPool(1);
        Collection<Callable<String>> tasks = new ArrayList<>();

        tasks.add(() -> {
            System.out.println("first task");
            TimeUnit.SECONDS.sleep(1);
            return "success";
        });

        tasks.add(() -> {
            System.out.println("second task");
            TimeUnit.SECONDS.sleep(2);
            return "success";
        });


        tasks.add(() -> {
            System.out.println("third task");
            TimeUnit.SECONDS.sleep(3);
            return "success";
        });
        // 任何一个任务执行完后,就会返回结果
        String result = service.invokeAny(tasks);
        System.out.println(result);
    }
}

五、关闭线程池

1. shutdown

  • 将线程池的状态改变为SHUTDOWN状态
  • 不会接受新任务,已经提交的任务不会停止
  • 不会阻塞调用线程的执行
void shutdown();
package com.dreamer.multithread.day09;

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

public class Demo04 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " first running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " second running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " third running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 不会阻塞主线程的执行
        pool.shutdown();
        System.out.println("main thread ending");

    }
}

2. shutdownNow

  • 不会接受新任务
  • 没执行的任务会打断
  • 将等待队列中的任务返回
List<Runnable> shutdownNow();
package com.dreamer.multithread.day09;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Demo04 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " first running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " second running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        pool.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + " third running");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 不会阻塞主线程的执行
        List<Runnable> leftOver = pool.shutdownNow();
        System.out.println(leftOver.size()); // 2
        System.out.println("main thread ending"); 

    }
}

六、线程池数量

  • 过小,导致cpu资源不能充分利用,浪费性能
  • 过大,线程上下文切换,导致占用内存过多,浪费性能

1. CPU密集型

  • 如果线程的任务主要是和cpu资源打交道,成为CPU密集型
  • 线程数量: 核心数+1
  • +1: 保证某线程由于某些原因(操作系统方面)导致暂停时,额外线程可以启动,不浪费CPU资源

2. IO密集型

  • IO操作,RPC调用,数据库访问时,CPU是空闲的,称为IO密集型
  • 线程数 = 核数* 期望cpu利用率* (cpu计算时间+cpu等待时间)/cpu计算时间

在这里插入图片描述

七、调度功能

1. 延时执行

  • 如果希望线程延时执行任务
package com.dreamer.multithread.day09;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo05 {
    public static void main(String[] args) {
        // 两个线程,分别可以延时执行不同的任务
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);

        // 延时1s后执行
        pool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " first running");
            }
        }, 1, TimeUnit.SECONDS);

        // 延时5s后执行
        pool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " second running");
            }
        }, 5, TimeUnit.SECONDS);
    }
}
package com.dreamer.multithread.day09;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo06 {
    public static void main(String[] args) {
        // 如果是单个线程,则延时的任务是串行执行的
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);

        // 如果一个线程出错,则会再次创建一个线程来执行任务
        pool.schedule(new Runnable() {
            @Override
            public void run() {
                int i = 1 / 0;
                System.out.println(Thread.currentThread().getName() + " first running");
            }
        }, 1, TimeUnit.SECONDS);

        pool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " second running");
            }
        }, 2, TimeUnit.SECONDS);
    }
}

2. 定时执行

  • scheduleAtFixedRate
  • scheduleWithFixedDelay
package com.dreamer.multithread.day09;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo07 {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);

        // 定时执行任务
        pool.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("task is running");
            }
        }, 3, 2, TimeUnit.SECONDS);
        //  初始延时,   任务间隔时间,    任务间隔时间单位
    }
}

八、异常处理

1. 不处理异常

  • 任务执行过程中,业务中的异常并不会抛出
package com.dreamer.multithread.day09;

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

public class Demo08 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                int i = 1 / 0;
                System.out.println(Thread.currentThread().getName() + " task running");
            }
        });
    }
}

2. 任务执行者自己处理

package com.dreamer.multithread.day09;

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

public class Demo08 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    int i = 1 / 0;
                    System.out.println(Thread.currentThread().getName() + " task running");
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
            }
        });
    }
}

3. 线程池处理

package com.dreamer.multithread.day09;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Demo08 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        Future<?> result = pool.submit(new Runnable() {
            @Override
            public void run() {
                int i = 1 / 0;
                System.out.println(Thread.currentThread().getName() + " task running");
            }
        });

        TimeUnit.SECONDS.sleep(1);
        // 获取结果的时候,就可以把线程执行任务过程中的异常报出来
        System.out.println(result.get());

    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值