Java线程池基础

目录

为何使用线程池而不直接创建线程:

简述线程池的工作步骤:

线程池的创建:

构造方法参数:

几种拒绝策略:

通过工厂模式创建线程池:

为何采用工厂模式:

 线程池执行效果:

简单模拟一个线程池:

线程池创建多少个线程合适:

结论:

原因:


为何使用线程池而不直接创建线程:

   线程池内有一些线程,用到线程执行任务时不再需要从系统申请新的线程,而是直接从池子中拿取使用,当使用完毕后还要还给池子。从线程池内拿取线程是用户态内的操作,而创建新的线程涉及到内核态操作,而内核只有一个,还需要支持其他程序运行,所以内核时非常忙的,不一定何时才能创建线程,所以创建线程的时间时不可控的。Java标准库提供了现成的线程池

简述线程池的工作步骤:

通过创建线程池,线程池内有内置的线程,其他线程通过submit方法向线程池内提交任务,把这些任务添加到一个阻塞队列中,然后线程池内的所有线程一直从阻塞队列中提取任务,并调用run方法,执行每一个任务。

线程池的创建:

构造方法参数:

corePoolSize:核心线程数

maximumPoolSize:最大线程数

KeepAliveTime:非核心线程的存活时间

unit:非核心线程的存活时间的单位

(任务较多的时候会创建一些非核心线程,任务少的时候过了非核心线程的存活时间后,非核心线程就会销毁)

workQueue:用来管理任务的阻塞队列,程序猿可以手动指定一个阻塞队列

threadFactory:线程工厂,用于创建线程的辅助的类

RejectedExecutionHandler:拒绝策略,即线程池满的时候采取拒绝新线程的拒绝策略

几种拒绝策略:

AbortPolicy:如果线程池内的任务满了,继续向线程池内添加任务直接报错

CallerRunsPolicy:如果线程池任务满了,继续向线程池内添加任务,让添加任务的线程自己执行该任务

DiscardOldestPolicy:抛弃最老的任务(即阻塞队列队首元素),让新添加的任务入队

DiscardPolicy:抛弃最新的任务(即阻塞队列队尾元素),让新添加的任务入队

通过工厂模式创建线程池:

        在绝大多数情况下,我们一般都是使用工厂模式创建线程池,而不是使用构造方法,以下为常用的几种线程池:

         //创建总线程数与核心线程数均固定为10的线程池,即意味着没有非核心线程,同一时间内最多有10个线程在活跃
        //当任务总数超过10的时候,会出现有任务在队列中等待空闲线程的出现,当有线程异常时,会自动补足线程,保持总线程数一直为10
        ExecutorService pool1 = Executors.newFixedThreadPool(10);
        //创建一个核心线程数为0的线程池,使用的都是非核心线程,如果非核心线程60秒内一直是空闲状态,就会销毁该线程
        //这样的线程池适合做一些短时间任务
        ExecutorService pool2 = Executors.newCachedThreadPool();
        //只有一个核心线程的线程池,没有非核心线程
        ExecutorService pool3 = Executors.newSingleThreadExecutor();
        //含有10个核心线程的定时器线程池,有schedule方法,类似与Timer的使用方法
        //区别在于Timer内只有一个线程,极大概率因为前置任务执行时间过长导致后续任务延期执行
        ScheduledExecutorService pool4 = Executors.newScheduledThreadPool(10);
        //含有一个核心线程的定时器线程,与Timer极其类似,都会因为前置任务执行时间过长导致后续任务延期
        //但是Timer内的线程如果挂了就全毁了,而该线程池内如果线程挂了,会有新的线程被创建用来顶替挂掉的线程
        ScheduledExecutorService pool5 = Executors.newSingleThreadScheduledExecutor();

为何采用工厂模式:

以实例化一个点(Point)对象为例子:我们都知道在二维平面中一个点可以通过xy坐标表示,也可以通过极坐标表示,然而这两种构造方法的参数表都是同样的类型,个数也都是一样的,不能构成方法重载,所以我们采取定义一个BuildPoint类,内置两种名字不同的方法来构造点对象:

 线程池执行效果:

 public static void main(String[] args) {
        //固定3线程的线程池
       ExecutorService threadPool = Executors.newFixedThreadPool(3);
        for (int i = 1; i < 10; i++) {
            int number = i;
            threadPool.submit(() -> {
                System.out.print("hello" + number + " ");
            });
        }
    }

 这里创建的线程池是固定3线程的线程池,main方法后续向该线程submit了9个任务,线程池内的三个线程合伙执行完这9个任务,因为线程调度的无序性,所以执行结果是无序的,第二次重新执行的结果极大概率也是不同的。

我们看到完成这9个任务后程序没有结束,这是因为线程池内3个线程是前台线程,且因为向队列中取出任务,但因为任务已经没有了,进入了阻塞状态

有一点需要注意的是,main方法内需要创建一个新的变量number来记录i才能打印i的值,因为变量捕获机制中捕获的变量必须是被final修饰的或者是实际final(没有被修改过)的,而i在一直修改,所以每次循环都需要number记录i,因为number没有变化过,所以number是可以被捕获的

简单模拟一个线程池:

class MyThreadPool{
    //用阻塞队列存储任务
    //让线程池内创建的线程取出阻塞队列中的任务,并在线程内调用run方法,从而可以实现有限的线程执行无限个添加的任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingDeque<>();
    public void submit(Runnable runnable) throws InterruptedException {
        //向阻塞队列中添加任务
        queue.put(runnable);
    }
    public MyThreadPool(int n) {
        //这里的n代表创建的线程的个数
        for (int i = 0; i < n; i++) {
            //创建n个线程,每个线程内从阻塞队列中取出任务,并调用任务的run方法
            //执行完一个任务后因为有while(true),就继续从阻塞队列中取出任务
            //这样的线程一共n个,相当于n个人的团队将一个大问题分解成小问题,每个人都解决小问题
            //一直做的没有问题可做
            Thread thread = new Thread(() -> {
                while (true){
                    try {
                        Runnable runnable = queue.take();
                        //没有为每个任务单独创建线程
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
        }
    }
}

线程池创建多少个线程合适:

结论:

        根据自己的项目一遍遍测试,根据运行时间与资源占用情况等选出效果最好的一种做法

原因:

        不同的程序内线程做的任务不一样

       有的线程做的任务是cpu密集型任务,主要做一些计算工作,需要在cpu上运行

        有的线程做的是IO密集型任务,主要是等待IO操作(等待读写硬盘,读写网卡等)这种任务不怎么占用cpu

        极端情况下如果所有的任务都是cpu密集型任务,那么线程数就不应该超过cpu的逻辑核心数,如果所有任务都是IO密集型任务,那么线程数应该远超过cpu核心数

但实际情况下,各种任务都有,所以需要手动测试

(这里的逻辑核心数指的是cpu内的逻辑处理器个数)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值