Java线程池的理解与实现

基本原理

和j2ee的连接池技术很像,都属于池化技术。在手写多个线程的过程中,经常会new好几个thread对象,也就是为线程对象申请资源,然后在线程运行完相应的代码后再释放资源
这一过程只有两三个还好,而当高并发的情形下,很有可能有几百上千个线程需要频繁的创建、销毁。这就导致了非常严重的系统资源浪费。
于是线程池技术应运而生,有一个容器一开始就装了很多的线程,需要用的时候从容器中拿出来,不需要的时候再放回容器,这样就避免了频繁申请资源,销毁线程而导致的资源浪费,这个容器就是线程池

使用Executors创建线程池

使用步骤

在这里插入图片描述
注意:线程池在创建之初默认是空的,默认能容纳int类型的最大值个线程

代码实现

public class MyThreadPool {
    public static void main(String[] args) throws InterruptedException {
        /**
         * Executors 用于创建线程池对象
         * ExecutorService 用于控制线程池
         */
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + " is running...");
        });
        Thread.sleep(2000);
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + " is running...");
        });
        executorService.shutdown();
    }
}

在这里插入图片描述
一些说明:
1、当前版本会让Executors.newCachedThreadPool()报红
原因是使用Executors创建线程池不会传入这个参数而使用默认值所以我们常常忽略这一参数,而且默认使用的参数会导致资源浪费,不可取。

2、为什么运行结果是2个相同的线程
因为在线程池中一个线程使用完后会自动归还到线程池中,当代码中

executorService.submit(()->{
	System.out.println(Thread.currentThread().getName() + " is running...");
});

执行完后,线程就应该归还给线程池,而这一个动作需要一定的时间,于是我们的sleep方法覆盖了这一时间,当再次执行这段代码块的时候,就会从线程中再取一个,此时线程1已经回到了池中,所以第二次执行时,仍然使用线程1

创建指定线程数量

使用Executors.newFixedThreadPoll()方法
这个方法默认创建的也是控线程池,但是可以在参数中指定线程个数的上限

使用ThreadPoolExecutor创建线程池

该实现方法的参数较为复杂,因此我们用一个实际的场景帮助理解

背景:某餐厅有服务员,分为正式员工和临时员工。当顾客特别多的时候,会招聘临时服务员,当顾客较少,等待一段时间之后(确保顾客不会增多了),解雇聘临时服务员,只有正式员工(万恶的资本家)。
我们抽象出如下核心元素——
在这里插入图片描述

参数详解

根据这些抽象元素,我们具体化为参数
参数一:核心线程数量
参数二:最大线程数
参数三:空闲线程最大存活时间
参数四:时间单位 — TimeUnit
参数五:任务队列 — 让任务在队列中等着,等有线程空闲了,再从这个队列中获取任务并执行。
参数六:创建线程工厂 — 按照默认的方式创建线程对象.
参数七:任务的拒绝策略

代码实现:

先看看这个类构造方法的的源码,该构造方法有七个参数,分别是上述三个参数

在这里插入图片描述

public class MyThreadPoolExecutor {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                2,
                5,
                2,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.shutdown();
    }
}

任务拒绝策略

1、默认淘汰策略AbortPolicy()

上述代码中AbortPolicy()是默认的淘汰策略,表示任务数量超过最大线程数(线程池+等待队列)即丢弃多余的任务。

注意:该方法在丢弃任务后会抛出RejectedExecutionException异常。
在这里插入图片描述

2、ThreadPoolExecutor.DiscardPolicy

特征:丢弃任务,但是不抛出异常这是不推荐的做法。

3、ThreadPoolExecutor.DiscardOldestPolicy

关键在于“oldest”
特征:抛弃队列中等待最久的任务然后把当前任务加入队列中。

4、ThreadPoolExecutor.CallerRunsPolicy

特征:调用任务的run方法绕过线程池直接执行
实现的策略是,当当前线程池无法分配足够的线程执行任务时,将把多余的任务交给别的线程完成
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值