JavaEE初阶--------第六章 总结线程池

系列文章目录

第六章 总结线程池


文章目录

  • 系列文章目录
  • 前言
  • 一、为什么要使用线程池
  • 二、线程池的参数介绍
    • 1、Java 标准库中创建线程池的方式
      • 3、线程池的七大参数
  • 三、模拟实现一个线程池
  • 四、线程池的工作流程
  • 总结


前言

前两章我们学习到了线程安全和使用 synchronized 来进行加锁操作。再次把视角拉回到线程,线程诞生的意义,是因为进程的创建/销毁都太“重量”了。但是如果进一步提高创建和销毁的频率时,那么线程的开销也就不能忽视了


一、为什么要使用线程池

  • 那么,该如何进一步来提高效率呢?
    两种典型的办法:
    1、协程(轻量级线程),相比于线程,把系统调度的过程给省略了。
    2、线程池,在使用第一个线程的时候,提前把2、3、4、5。。。线程创建好,后续如果想使用新的线程,不必重新创建了,直接拿过来就能用了。

  • 为啥直接从池子中取的效率比新创建线程的效率更高?
    从池子中取,这个动作是纯用户态的操作。
    创建新的线程,这个动作则是需要用户态 + 内核态相互配合来完成的操作。
    (如果一段程序,是在系统内核中执行,此时就称为“内核态”,如果不是则称为“用户态”)

  • 这样一种纯用户态的操作,是非常可控的,所以就能优化频繁创建销毁线程的场景

二、线程池的参数介绍

1、Java 标准库中创建线程池的方式

public class Demo12 {
    public static void main(String[] args) {
        ExecutorService service1 = Executors.newCachedThreadPool();

        ExecutorService service2 = Executors.newFixedThreadPool(10);

        ExecutorService service3 = Executors.newSingleThreadExecutor();

        ExecutorService service4 = Executors.newScheduledThreadPool(100);
    }
}

大家看到上述线程池对象的创建不知道第一眼会不会感觉有点奇怪。是的,线程池对象不是直接 new 出来的,而是通过一个专门的方法,返回了一个线程池对象,这其实就是工厂模式

JavaEE初阶--------第六章 总结线程池_第1张图片

创建方式 描述
Executors.newCachedThreadPool(); 创建线程数目动态增长的线程池
Executors.newFixedThreadPool(10); 创建固定线程数的线程池
Executors.newSingleThreadExecutor(); 创建只包含单个线程的线程池
Executors.newScheduledThreadPool(100); 设定延迟时间后执行命令,或者定期执行命令,是进阶版的 Timer

3、线程池的七大参数

  • 上述这四个工厂方法生成的线程池,本质上都是对 ThreadPoolExecutor 类进行的封装,也就是说== Executors 本质上是 ThreadPoolExecutor 类的封装==。
    构造方法中的参数有很多,这也是接下来需要了解的重点
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, 
				   long keepAliveTime, TimeUnit unit,
 				   BlockingQueue<Runnable> workQueue, 
 				   ThreadFactory threadFactory, 
				   RejectedExecutionHandler handler)
  • int corePoolSize,int maximumPoolSize 描述了线程池中线程的数目。前者是核心线程数,后者是最大线程数。说明这个线程池里线程的数目是可以动态变化的,变化范围就是 [corePoolSize , maximumPoolSize]

  • long keepAliveTime 超时时间,有人使用会自动释放TimeUnit unit 超时单位

  • BlockingQueue workQueue, 阻塞队列,用来存放线程池中的任务。可以根据需要灵活设置这里的队列是啥

  • ThreadFactory threadFactory, 工厂模式的体现,此处使用 ThreadFactory 作为工厂类,由这个类负责创建线程,主要是为了在创建过程中,对线程的属性作出一些设置

  • RejectedExecutionHandler handler 线程池的拒绝策略(重要):一个线程池能容纳的任务数量是有上限的,当持续往线程池里添加任务的时候,一旦已经达到上限了,继续再添加,会出现什么效果?

  • 拒绝策略
    JavaEE初阶--------第六章 总结线程池_第2张图片

  • AbortPolicy:直接抛出异常

  • CallerRunsPolicy:新添加的任务,由添加任务的线程负责执行

  • DiscardOldestPolicy:丢弃任务队列中最老的任务

  • DiscardPolicy:丢弃当前新加的任务

  • 使用线程池,需要设置线程的数目,数目设置多少合适?
    一个线程执行的代码主要有两类:
    1.CPU 密集型:代码里主要的逻辑是在进行算术运算/逻辑判断
    2.IO 密集型:代码里主要进行的是 IO 操作
    所以,代码不同,线程池的线程数目设置不同。正确做法:使用实验的方式,对程序进行性能测试

三、模拟实现一个线程池

class MyThreadPool3{
    //描述一个类,直接使用Runnable
    //使用一个数据结构来组着若干个任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    //描述一个线程,工作线程的功能就是从任务队列中取任务并执行
    static class Worker extends Thread{
        //当前线程池中有若干个worker线程,这些线程内部都持有上述的任务队列
        private BlockingQueue<Runnable> queue = null;

        public Worker(BlockingQueue<Runnable> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            while (true){
                try {
                    //循环的获取任务队列中的任务
                    //如果队列为空直接阻塞,如果不为空,就获取里面的内容
                    Runnable runnable =  queue.take();
                    //获取到之后,就执行任务
                    runnable.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //创建一个数据结构来组织若干个线程
    private List<Thread> workers = new ArrayList<>();

    public MyThreadPool3(int n){
        //在构造方法中创建若干个线程放到数组中
        for (int i = 0; i < n; i++) {
            Worker worker = new Worker(queue);
            worker.start();
            workers.add(worker);
        }
    }
    //创建一个方法,允许程序员放任务到线程池中
    public void submit(Runnable runnable){
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Demo14 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(10);
        for (int i = 0; i < 100; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello threadpool");
                }
            });
        }
    }
}

JavaEE初阶--------第六章 总结线程池_第3张图片

JavaEE初阶--------第六章 总结线程池_第4张图片
JavaEE初阶--------第六章 总结线程池_第5张图片

JavaEE初阶--------第六章 总结线程池_第6张图片

  • 线程池这边会创建是个 worker ,这10个 worker 都持有我的任务池,各自不停地从我的任务池当中拿任务去执行。等到线程池当中没有任务了,这10个 worker 就阻塞了。

四、线程池的工作流程

JavaEE初阶--------第六章 总结线程池_第7张图片


总结

本章我们学习了线程池的基本知识,线程池是优化频繁创建/销毁线程的场景而诞生的,进一步地提高了效率。Java标准库中的线程池有四大工厂方法,七大参数,四大拒绝策略,这些都是线程池最基本也是最重要的内容。

你可能感兴趣的:(java-ee,java,开发语言)