线程池基本概念与使用

1、线程池的使用场景:

    线程池做的工作主要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

    主要特点:线程复用、控制最大并发数、管理线程

    降低资源消耗、提高响应速度:重复利用已创建的线程降低线程创建和销毁造成的消耗,同时提高速度。

    提高线程的可管理性:线程使稀缺资源,使用线程池可以统一的分配、调优和监控。

2、线程池工作原理:

    当线程池中数量小于核心线程数corePoolSize时,新提交的任务创建新线程执行任务,即使存在空闲的线程;

    当线程数据>=核心线程数corePoolSize时,新提交任务将放入workQueue工作队列中,等待线程池任务调度执行;

    当workQueue工作队列已满,且MaximumPoolSize>CorePoolSize,新提交的任务会创建新的非核心线程执行任务;

                                         已满,且MaximumPoolSize<=CorePoolSize,新提交的任务由RejectedExecutionHandler处理;

    当线程池中线程数量超过corePoolSize,空闲时间达到keepAliveTime时,关闭空闲的非核心线程,最终线程数量可能收缩到corePoolSize大小。

当设置的allowCoreThreadTimeOut(true)时,线程池中核心线程空闲时间达到keepAliveTime,也将关闭核心线程。

拒绝策略:RejectedExecutionHandler

    AbortPolicy,线程池的默认策略;如果线程池队列满了,丢掉这个任务并且抛出RejectedExectionException异常。

    DiscardPolicy,如果线程池队列满了,直接丢掉这个任务,且不会有任何异常。

    DiscardOldestPolicy,丢弃最老的,即如果队列满了,会将最早进入队列的任务删掉腾出空间。队列先进先出,每次移除队头元素后再尝试入队。

    CallerRunsPolicy,如果添加到线程池师表,那么主线程自己去执行任务。就像个急脾气的人做事情一样。

    自定义拒绝策略,实现RejectedExecutionHandler接口,实现rejectedExecution方法

线程池分类:

线程池核心:

一、什么是线程池?

java.util.concurrent.Executors提供了一个java.util.concurrent.Executor接口的实现用于创建线程池。减少处理器闲置时间,增加吞吐能力。

一个服务器完成一项任务需要时间:T1创建线程的时间;T2在线程中执行任务的时间;T3销毁线程的时候。使用线程池可缩短T1和T3

线程池包含以下四个基本组成部分:

1、线程池管理器(ThreadPool):创建并管理线程池,包括:创建线程池、销毁线程池,添加新任务;

2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;

3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;

4、任务队列(tashQueue):用于存放没有处理的任务。提供一种缓冲机制;

二、常见线程池

newSingleThreadExecutor

单个线程的线程池,单线程串行

newFixedThreadExecutor(n)

固定数据的线程池,当达到最大数量

newCacheThreadExecutor (推荐)

newScheduleThreadExecutor

在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池;

然而阿里规约强制要求手动创建线程池,不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

static void staticPoolCreate() {

        //java doc建议使用Executors创建线程池
        //阿里巴巴规范规定不允许使用Executors创建线程池,因线程池是珍贵的系统资源,每个参数都需斟酌,不可滥用
        ExecutorService executor1 = Executors.newCachedThreadPool();
        ExecutorService executor2 = Executors.newSingleThreadExecutor();
        ExecutorService executor3 = Executors.newFixedThreadPool(10);

    }

三、线程池常用任务队列

    ArrayBlockingQueue:一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序

    LinkedBlockingQueue: 一个基于链表结构的阻塞队列,此队列按照FIFO排序元素,吞吐量要高于ArrayBlockingQueue。静态工厂方法固定线程池和单例线程池使用了这个队列。

    SynchronousQueue: 一个不存储元素的阻塞队列,吞吐量高于LinkedBlockingQueue,每个插入操作必须等到另一个线程调用移除操作。

    PriorityBlockingQueue: 一个具有优先级的阻塞队列,该队列可以控制任务的执行先后顺序,根据任务自身的优先级顺序先后执行。

队列操作:add   remove  offer  poll  put   take element   peek 

标签下发任务:任务配置表、任务配置扩展表、任务缓存队列表

1、定义一个线程池配置confi:ThreadPoolTaskExecutor,位于org.springframework.scheduling.concurrent包下,设置核心配置:核心线程数、最大线程数、缓存队列数、允许线程空闲时间、线程池前缀名、销毁机制

2、注入线程池executeTask(thread),统一管理

3、@Scheduled设置4分钟循环,拿到全部任务,创建线程,丢给2定义的线程池执行,线程内部逻辑,插入任务缓存表

4、每30分钟一次,推送任务

四、实际使用

public class ThreadPoolTest {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 1, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(5), new ThreadPoolExecutor.AbortPolicy());
//        execute(executor);
        submit(executor);
    }

    //带参数返回
    static void submit(ThreadPoolExecutor executor) {
        List<Future<String>> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            final String index = String.valueOf(i);
            Future<String> future = executor.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println("执行:" + index);
                    Thread.sleep(10);
                    return index;
                }
            });
            list.add(future);
        }

        for (Future<String> future : list) {
            try {
                String result = future.get();
                System.out.println("获得结果:" + result);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        executor.shutdown();
    }

    //无参数返回
    static void execute(ThreadPoolExecutor executor) {
        for (int i = 0; i < 11; i++) {
            MyTask myTask = new MyTask(i);
            executor.execute(myTask);
            System.out.println("线程池线程数目:" + executor.getPoolSize() +
                    ",队列中等待的任务数目:" + executor.getQueue().size() +
                    ",已执行完成数目:" + executor.getCompletedTaskCount());
        }
        try {
            Thread.sleep(10 * 500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executor.shutdown();
    }

}

class MyTask implements Runnable {
    private int taskNum;

    public MyTask(int taskNum) {
        this.taskNum = taskNum;
    }

    @Override
    public void run() {
        try {
            System.out.println("正在执行task:" + taskNum);
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("task:" + taskNum + "执行异常");
        }
        System.out.println("task:" + taskNum + "执行完毕");
    }
}

1、使用execute(thread),执行一个任务,没有返回值;

正在执行task:0
线程池线程数目:1,队列中等待的任务数目:0,已执行完成数目:0
线程池线程数目:2,队列中等待的任务数目:0,已执行完成数目:0
正在执行task:1
线程池线程数目:3,队列中等待的任务数目:0,已执行完成数目:0
线程池线程数目:3,队列中等待的任务数目:1,已执行完成数目:0
正在执行task:2
线程池线程数目:3,队列中等待的任务数目:2,已执行完成数目:0
线程池线程数目:3,队列中等待的任务数目:3,已执行完成数目:0
线程池线程数目:3,队列中等待的任务数目:4,已执行完成数目:0
线程池线程数目:3,队列中等待的任务数目:5,已执行完成数目:0
线程池线程数目:4,队列中等待的任务数目:5,已执行完成数目:0
正在执行task:8
线程池线程数目:5,队列中等待的任务数目:5,已执行完成数目:0
正在执行task:9
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.yg.edu.pool.MyTask@eed1f14 rejected from java.util.concurrent.ThreadPoolExecutor@7229724f[Running, pool size = 5, active threads = 5, queued tasks = 5, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at com.yg.edu.pool.ThreadPoolTest.execute(ThreadPoolTest.java:57)
	at com.yg.edu.pool.ThreadPoolTest.main(ThreadPoolTest.java:17)
task:0执行完毕
正在执行task:3
task:2执行完毕
task:1执行完毕
task:9执行完毕
task:8执行完毕
正在执行task:6
正在执行task:5
正在执行task:4
正在执行task:7
task:3执行完毕
task:4执行完毕
task:5执行完毕
task:6执行完毕
task:7执行完毕

从结果可知,初始创建新线程,直到线程数达到核心线程数3,新的任务加到了阻塞队列中,当阻塞队列达到最大值5时,继续创建新的线程,直到线程数等于最大线程数5,表示线程池满了,再有新的任务则,执行拒绝策略;

2、使用submit(Callable<T> task),它的返回值是Future<T> ,可以通过future.get()获取结果。future.get()方法会使当前线程阻塞,直到执行的线程返回结果后,当前线程才会被唤醒;

执行:2
执行:0
执行:1
执行:3
执行:4
执行:5
获得结果:0
获得结果:1
获得结果:2
执行:6
执行:7
执行:8
获得结果:3
获得结果:4
获得结果:5
执行:9
获得结果:6
获得结果:7
获得结果:8
获得结果:9

从结果可知,执行的顺序不是完全从0到9,获得的结果严格按照顺序,从0到9;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值