线程池

在当前实际开发中,我们已经很少见到能单独使用Thread或Runable的场景。更多的是用线程池。线程池即会初始化创建一些线程,这些线程当任务来临的时候,会立即去处理任务,在任务完成后也不会进行销毁,而是等待下一个任务的到来。
1.为什么要使用线程池?

(1)线程的创建和销毁都需要时间,那么理论上我们创建线程执行任务,包含的时间是 创建的时间T1、执行任务的时间 T2、销毁的时间T3。所以执行一个任务的时间成本是T1+T2+T3。线程池的线程只包含了执行了时间。理论上是T2。

(2)线程的频繁创建和杂乱无章的分布,对线程的管理性极差,极易造成CPU的浪费。

(3)线程池会降低资源的消耗,降低创建和销毁的资源消耗。



2.实现简易版线程池

在写线程池前,要捋清Thread和Runable的关系。Runable只是给你接口让你实现工作线程的工作事务,然后附加到你new thread的线程上,其本身并不会创建线程

线程池

package ThreadPool;

import java.util.concurrent.ArrayBlockingQueue;

public class MyThreadPool {
    //初始化线程的数量
    private Integer InitThreads;
    //阻塞队列的边界值
    private Integer QueueCount;
    //初始阻塞队列
    private ArrayBlockingQueue<Runnable> tasks;
    //初始工作线程队列
    private WorkThread[] workThreads;


    //运行用户自行赋值
    public MyThreadPool(Integer InitThreads,Integer QueueCount){
        if (InitThreads<=0){
            InitThreads=5;
        }
        if (QueueCount<=0){
            QueueCount=100;
        }
        tasks=new ArrayBlockingQueue<Runnable>(QueueCount);
        workThreads=new WorkThread[InitThreads];
        //并且在线程中初始化线程并启动
        for (int i=0;i<workThreads.length;i++){
            workThreads[i]=new WorkThread();
            workThreads[i].start();
        }
    }

    //任务进入线程池的方法
    public void putTask(Runnable rs){
        tasks.add(rs);
    }

    //线程池销毁的方法
    public void destroy(){
        //让工作池中的每个线程都进行中断操作
       for (int i=0;i<workThreads.length;i++){
           workThreads[i].interrupt();
       }
    }


    //工作线程
    class WorkThread extends Thread{
        public void run(){
            Runnable rs=null;
            //如果此线程没有被阻塞,那么就要去完成队列的任务,一直到队列任务都被完成为止
            while (!isInterrupted()){
                rs=tasks.poll();
                if (rs!=null) {
                    rs.run();
                    System.out.println(rs+"is start run");
                }
            }
        }
    }
}

任务Runnable

package ThreadPool;

public class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name){
        this.name=name;
    }
    @Override
    public void run() {
        try {
            System.out.println(name+" 被放入任务队列中...");
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Main测试类

package ThreadPool;

public class Main {
    public static void main(String[] args) {
        //实例化连接池
        MyThreadPool pool=new MyThreadPool(5,20);
        for (int i=0;i<10;i++){
            pool.putTask(new MyRunnable("Task"+i));
        }
    }
}

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

在简易版线程池中,面临最大的问题是,不能根据任务的数量,启动的线程进行自适应的调节,避免大量线程在空启动。没有任务给它执行。造成了资源的浪费。



3.线程池和连接池的不同?

在学线程池前,我们肯定了解过连接池,连接池的设计思想就是,我们初始化一些连接,如果有需要连接的用户,就会去连接池取出连接,用完后再还回去。但这种思想在线程池中肯定不行,为什么呢?因为线程一旦运行完就结束,取出来已启动好的进行运行,当这个任务执行完,线程也就结束了,这样池的核心思想也就实现不了。

那么它们之间有什么区别呢?
1.连接池是每次需要时候的时候,从池里取出一个连接给我们,我们再使用这个连接来交换数据,而线程池并不是在使用的时候从池里取出一个线程对象给我们使用,而是将我们的任务交给线程池,由线程池自己调度任务决定什么时候执行这个任务。
2.连接池的连接用完之后,会直接放到池内,等待下一个请求连接,而线程池的线程一旦运行完,线程也就结束了,因此不存在放回池内的操作。
3.一个连接池要持有一定数量的连接,只要保证持有这些未关闭的连接的对象即可,而线程池要持有一定数量的线程,必须保证持有的线程的run方法不会运行结束。

总结来说也就是,会有一个阻塞队列作为中间件,线程池会有一些初始化好的线程,任务首先进入阻塞队列,当线程池还有空闲的线程,池会从阻塞队列取出任务进行执行,当任务数大于线程数,这时候会设定一个范围进行扩容初始线程。





4.JDK中线程池是如何工作的?

在这里插入图片描述
1.线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
2.当调用 execute() 方法添加一个任务时,线程池会做如下判断:
(1)如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
(2)如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
(3)如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
(4)如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
3.当一个线程完成任务时,它会从队列中取下一个任务来执行。
4.当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

提交任务:
execute(Runable command); 没有返回值
Future submit(Callable task); 有返回值

关闭线程池:
shutdown():设置线程池的状态为关闭,只会中断没有执行任务的线程。
shutdownNow():设置线程池的状态关闭,还会尝试停止正在运行或者暂停运行的线程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值