Java中的线程池

线程池是什么

  • 线程池与字符串常量池一样,也是为了提高效率。
  • 虽然线程是轻量的,但是在频繁的创建 / 销毁线程的情况下, 开销是不可忽略的。
  • 线程池就是提前创建好线程,需要用到线程时,不再去创建线程,而是使用线程池提前准备好的线程。

从线程池里拿线程为什么比从系统创建线程要更高效?

从线程池里拿线程是纯粹的用户态操作,而从系统创建线程是内核态与用户态之间的切换,操作系统的内核要为很多应用程序提供服务,有的时候服务不一定及时,所以从系统创建线程更低效,时间不可控。

线程池的好处

  • 降低资源消耗:减少线程的创建和销毁带来的性能开销。
  • 提高响应速度:当任务来时可以直接使用,不用等待线程创建
  • 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。

创建线程池

Executor 是一个工厂类, ExecutorService 表示一个线程实例

Executor 类创建线程池的几种方式, 返回值是 ExecutorService 对象

  1. 创建固定线程数的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
  1. 创建线程数目动态增长的线程池
ExecutorService pool = Executors.newCachedThreadPool();
  1. 创建只包含当个线程的线程池
ExecutorService pool = Executors.newSingleThreadExecutor();
  1. 设定延时时间后执行命令, 或者定期执行命令, 是进阶版的 Timer (定时器)
ExecutorService pool = Executors.newScheduledThreadPool(10);
  • 观察这些创建方式,可以发现创建线程没有直接 new 对象,而是调用 Executors 类里的静态方法来创建线程池。

这是工厂设计模式,是为了解决构造方法的缺陷的(有的场景下,需要构造方法的参数一样,但这是不允许的),不直接 new 对象,
通过工厂方法(一般是静态方法)来协助我们来创建对象。

ThreadPoolExecutor 是原装的线程池类, 上述的工厂方法就是对这个类进行封装

ThreadPoolExecutor 类

构造方法:
在这里插入图片描述

参数说明:

  1. corePoolSize 核心线程数 (始终存活的线程)
  2. maximumPoolSize 最大线程数 (核心线程数+临时线程数,任务比较多时, 会多创建一些线程, 任务较少时, 会结束一些线程)
  3. keepAliveTime 保持存活时间 (任务较少时, 比较空闲, 临时线程保持多久)
  4. unit 保持存活时间的单位, ms, s
  5. workQueue 可以手动传一个阻塞队列进去, 线程池里要管理很多任务, 这些任务也是通过队列来组织的
  6. threadFactory 创建线程的工厂, 参与具体的创建线程工作(工厂类)
  7. handler 线程池的拒接策略, 如果线程池满了, 继续添加任务, 如何拒绝

拒绝策略:

  1. 抛出一个抛出一个 RejectedExecutionException 异常
    ThreadPoolExecutor.AbortPolicy()
  2. 让添加任务的线程自己负责执行这个任务
    ThreadPoolExecutor.CallerRunsPolicy()
  3. 丢弃最旧的未处理任务(队首任务)
    ThreadPoolExecutor.DiscardOldestPolicy()
  4. 丢弃最新任务(丢弃被拒绝的任务)
    ThreadPoolExecutor.DiscardPolicy()

直接使用构造方法 new 一个线程池:

// 自定义创建线程池
ExecutorService pool = new ThreadPoolExecutor(
                10, // 核心线程数
                100,// 最大线程数
                1000,// 最大存活时间
                TimeUnit.MILLISECONDS,// 单位
                new LinkedBlockingDeque<>(),// 阻塞队列
                new ThreadPoolExecutor.AbortPolicy()// 拒绝策略
        );

线程池的简单实现

代码:

public class MyThreadPool {
    // 阻塞队列存放任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    // 添加任务
    public void submit(Runnable runnable) {
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 构造方法, 实现一个固定线程数的线程池
    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread thread = new Thread(() -> {
                while (true) {
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            // 启动线程
            thread.start();
        }
    }

    public static void main(String[] args) {
        // 创建一个固定10个线程的线程池
        MyThreadPool myThreadPool = new MyThreadPool(10);
        for (int i = 0; i < 30; i++) {
            int n = i;// 实际final
            // 向线程池添加任务
            myThreadPool.submit(() -> {
                System.out.println("hello" + n);
            });
        }
    }
}

执行结果:
在这里插入图片描述

  • 线程池中的10个线程无序调度, 执行任务顺序和添加任务顺序不一致很正常
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值