线程池知识小结

为什么要有线程池?

  • 单个线程的创建时它会开辟本地方法栈、虚拟机栈、程序计数器成为线程的私有内存,因此频繁的创建和消耗会耗费系统的资源
  • 在任务量远远大于线程可以处理的任务量时,并不能友好的拒绝任务。
    基于上面的问题我们引入了线程池(采用池化技术来管理和使用线程),线程池相对于线程来说是长生命周期的。线程池有两个重要的角色是线和任务队列。

线程池的优点

线程池其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
合理使用线程池能够带来的以下好处:

  • 降低资源消耗,减少了创建和销毁线程的次数(每个工作线程都可以被重复利用,可执行多个任务)
  • 提高响应速度,当任务到达时,任务可以不需要等线程创建就能立即执行
  • 可以更好的管理线程的数量和资源的个数
  • 线程池可以执行定时任务和拒绝不能处理的任务

线程池的执行流程

在这里插入图片描述

线程池的执行方式

  • 1.执行的任务无返回值 execute(new Runnable)execute执行任务如果有OOM异常会将异常打印到控制台
  • 2.执行的的任务有返回值 submit(new Runnable 无返回值/ new Callable 有返回值)execute执行任务如果有OOM异常不会将异常打印到控制台

线程池的关闭

  • shutdown:拒绝新任务加入,等待线程池中的任务队列执行完之后在停止线程池
  • shutdownNow:拒绝执行新任务,并且会立即停止不会等待任务队列中的任务执行完,才停止线程池

线程池的状态

线程池共有五种状态。要与线程的状态分开哦。
在这里插入图片描述

  • RUNNING:这个没什么好说的,这是最正常的状态:接受新的任务,处理等待队列中的任务;
  • SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务;
  • STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执⾏任务的线程;
  • TIDYING:所有的任务都销毁了,workCount 为 0。线程池的状态在转换为 TIDYING 状态时,会执行钩子⽅法terminated();
  • TERMINATED:terminated() ⽅法结束后,线程池的状态就会变成这个。

线程池的创建

Java里面线程池的顶级接口是 java.util.concurrent.Executor (JUC),但是严格意义上讲 Executor 并不是一个线程 池,而只是一个执行线程的工具。真正的线程池接口是 java.util.concurrent.ExecutorService

ThreadPoolExecutor创建

这种是最经典的线程池创建方式,也时最常用的方式,这种方式可以
优点

  • 这种方式可以解决线程数量不可控的问题
  • 这种方式可以解决任务数量不可控的问题
public class ThreadPoolExecutorTest {

    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                5,//核心线程数--->正式员工
                10,//最大线程数--->正式员工+临时工
                60,
                TimeUnit.SECONDS,//idle线程的空闲时间:临时工最大的存活时间,超过时间就解雇
                new LinkedBlockingQueue<>(),//阻塞队列:任务存放的地方(快递仓库)
                new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable r) {//线程池中定义的任务类r
                        return new Thread(new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(Thread.currentThread().getName() + "开始执行了");
                                //r对象是线程池内部封装过的工作任务类(Worker),会一直循环等待的方式从阻塞队列中取任务来执行
//                                r.run();
                            }
                        });
                    }
                },//创建线程的工厂类:线程池创建线程时,调用该工厂的方法创建线程--->招聘员工的标准
                new ThreadPoolExecutor.AbortPolicy()
                /**
                 * 拒绝策略:达到最大线程数且阻塞队列已满,采取的拒绝策略
                 * AbortPolicy:直接抛RejectedExecutionException(不提供handler时的默认策略)
                 * CallerRunsPolicy:谁(某个线程)交给我(线程池)任务,我拒绝执行,由谁自己执行
                 * DiscardPolicy:交给我的任务,直接丢弃掉(尾删)
                 * DiscardOldestPolicy:丢弃阻塞队列中最旧的任务(头删)
                 */
        );
    }
}

7个参数:

  1. corePoolSIze:所谓的核⼼线程数,可以⼤致理解为⻓期驻留的线程数⽬
  2. maximumPoolSize:顾名思义,就是线程不够时能够创建的最⼤线程数。
  3. keepAliveTime:空闲线程的保活时间,如果线程的空闲时间超过这个值,那么将会被关闭。注意此值 ⽣效条件必须满⾜:空闲时间超过这个值,并且线程池中的线程数少于等于核⼼线程数
  4. TimeUnit:保活时间单位。
  5. BlockingQueue:任务丢列,⽤于存储线程池的待执⾏任务的。
  6. threadFactory:⽤于⽣成线程,⼀般我们可以⽤默认的就可以了。或者自定义我们需要的任务。
  7. handler:拒绝策略,当线程池已经满了,但是⼜有新的任务提交的时候,该采取什么策略由这个来指定。有⼏种 ⽅式可供选择,像抛出异常、直接拒绝然后返回等,也可以⾃⼰实现相应的接⼝实现⾃⼰的逻辑。

5种拒绝策略
拒绝策略:达到最大线程数且阻塞队列已满,采取的拒绝策略

  1. AbortPolicy:直接抛RejectedExecutionException(不提供handler时的默认策略)
  2. CallerRunsPolicy:谁(某个线程)交给我(线程池)任务,我拒绝执行,由谁自己执行
  3. DiscardPolicy:交给我的任务,直接丢弃掉(尾删)
  4. DiscardOldestPolicy:丢弃阻塞队列中最旧的任务(头删)
  5. 我们自定的拒绝策略,可以写进日志里或者存储到数据库当中

下面介绍的方法都不推荐,可能会造成内存溢出(OOM)的问题,在JAVA开发手册中明确规定了不允许
在这里插入图片描述

FixedThreadPool创建

创建一个固定个数的线程池

ExecutorService executorService =
                Executors.newFixedThreadPool(10);

SingleThreadExecutor

创建单个线程的线程池,上一种创建线程的单机版本

        ExecutorService service = Executors.newSingleThreadExecutor();
     

创建单个线程池有什么用呢?·

  • 可以避免频繁创建和销毁线程带来的性能开销
  • 有任务队列是可以存储多余的任务
  • 当任务量过大时 可以有好的拒绝
  • 线程池可以更好的管理任务

CachedThreadPoo

创建带缓存的线程池,适用于短期有大量任务的时候。

ExecutorService executorService =
            Executors.newCachedThreadPool();

FixedThreadPool(10, threadFactory)

自定义规则的线程池

 public static void main(String[] args) {
        // 自定义线程工厂
        MyThreadFactory threadFactory = new MyThreadFactory();
        ExecutorService executorService =
                Executors.newFixedThreadPool(10, threadFactory);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    Thread thread = Thread.currentThread();
                    System.out.println("线程名:" +
                            thread.getName() +
                            ",优先级:" + thread.getPriority());
                }
            });
        }
    }
    private static int count = 1;
    static class MyThreadFactory implements ThreadFactory {

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            // 自定义线程池的名称规则
            thread.setName("mythreadpool-" + count++);
            // 设置优先级
            thread.setPriority(10);
            return thread;
        }
    }

ScheduledThreadPool

创建执行定时任务的线程池必须传递一个参数

ublic static void main(String[] args) {
        // 创建执行定时任务的线程池
        //必须要传递一个参数
        ScheduledExecutorService scheduledExecutorService =
                Executors.newScheduledThreadPool(1);
        System.out.println("设置定时任务:" + new Date());
        // 执行定时任务

//参数 1任务 2延迟多少时间执行任务 4时间的单位
//只会执行一次
          scheduledExecutorService.schedule(new Runnable() {
              @Override
              public void run() {
                  System.out.println("执行任务:" + new Date());
              }
          }, 1, TimeUnit.SECONDS);

//参数 1任务 2延迟多少时间执行任务 3执行的频率 4时间的单位
//以上次任务的开始时间就开始计时,作为计算下一次任务的开始时间
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
              @Override
              public void run() {
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("执行任务:" + new Date());
              }
          }, 1, 3, TimeUnit.SECONDS);
//参数 1任务 2延迟多少时间执行任务 3执行的频率 4时间的单位
//以上次任务的结束时间开始计时,作为下次任务的开始时间     
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:" + new Date());
            }
        }, 1, 3, TimeUnit.SECONDS);

    }

SingleThreadScheduledExecutor

创建单个执行定时任务的线程池,上一种创建方式的单机版本

ScheduledExecutorService service =
                Executors.newSingleThreadScheduledExecutor();

newWorkStealingPool

JDK8之后会根据当前CPU生成对应个数的线程池,并且是异步执行的
因为是异步,所以创建出来便结束

  public static void main(String[] args) {
        // 创建一个异步根据当前CPU生产的线程池
        ExecutorService service = Executors.newWorkStealingPool();
        for (int i = 0; i < 10; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:" +
                            Thread.currentThread().getName());
                }
            });
        }

        // 等待线程池执行完成
        while (!service.isTerminated()) {
        }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值