你真的知道使用Java的线程池吗?

  • Post author:
  • Post category:java




线程调度之线程池

创建一个线程,销毁一个线程,都会消耗一定量的CPU资源。当线程创建得越来越频繁,而系统的CPU已经分配不了那么多的资源,可能会变成一种等待的状态,使用起来非常不方便。那么有没有什么方法可以直接现取现用不需要等待,提高线程调度的效率呢?



线程池的概念

我们在学习Java时不免要认识到很多池,例如:字符串常量池,数据库连接池等其他池。


字符串常量池

:将经常会用到的字符串存储在一个‘池’一样的地方,当需要使用时,直接拿取,

不需要在内存中开辟一个空间

存放该内容!


数据库连接池

:负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是

再重新建立一个

线程池的创建:

//Executors.newCachedThreadPool :线程池
 ExecutorService pool = Executors.newFixedThreadPool(10);//设定线程数
pool.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
    }
});

同样的,线程池也是这样的一个存在,当你需要足够多的资源,而又不想消耗太多CPU,就可以使用线程池提高效率,接下来我由两个方面来介绍线程池对效率的提升:

  1. 线程的普通创建是在内核态的,然后由

    内核态转换为用户态

    ,这样的转换不得已会消耗资源,虽然看起来非常小,但是与

    直接在用户态

    中拿取线程对比,效率的确要略低一筹。
  2. 线程池是Java官方提供的线程池,肯定是从各个方面进行了优化,其中可以调用的线程数也有最大值,不过一般使用到最大值。



工厂模式

线程池的工厂模式,解决构造方法的‘坑’,当我们创建线程时,会调用构造方法,而我们有时候不知道要传递什么参数来调用构造方法,而这个工厂模式,就可以解决这样的问题。

工厂模式的使用,官方给出的使用方法:

在这里插入图片描述


参数解读

int corePoolSize 核心线程数(最重要的线程数)
int maximumPoolSize 最大线程数
int keepAliveTime 保持活跃的线程数(除核心线程数的其他线程)
Timeunit unit (除核心线程数的其他线程)存活数
BlockingQueue workQueue 线程池中需要管理许多任务,通过阻塞队列进行管理
ThreadFactory threadFactory 线程工厂,创建线程的辅助类
RejectedExecutionHandler handler 线程池的拒绝策略


线程池的拒绝策略

(敲黑板,面试常考):

AbortPolicy 直接抛出异常
CallerRunsPoilcy 由添加的线程自己执行该任务(对自己的行为负责)
DiscardOldestPolicy 丢弃最久的任务,接收新任务
DiscardPolicy 丢弃最新任务,也就是不能接收当前任务

使用工厂模式创建对象时,不需要直接

new

,使用其静态方法直接创建需要的对象,为何可以使用不同的构造方法,是因为构造方法重载了。在创建线程时可以填写数字来确定创建的线程数!不过不填写会根据代码需求来创建线程数。


实现一个线程池

class MyThreadPool {
    //用于存放任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingDeque<>();
    //给任务池中添加任务
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }

    //指定创建线程数
    public MyThreadPool(int n){
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(()->{
                //只要有任务就一直取
                while (true) {
                    try {
                        Runnable r = queue.take();
                        r.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            //启动线程
            t.start();
        }
    }
}

使用实现的线程池:

class Main{
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int number = i;//变量捕获
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("The number: "+number);
                }
            });
        }
    }
}

这里的number是一个实际final,所以这里要用number来代替i。


在实际开发中,线程的数量是如何确定的

解答这个问题,我们需要明白程序的工作,一般程序的任务分为两类:

  1. CPU密集性任务,要在CPU上运行,对性能要求很高
  2. IO密集性任务,主要是等待IO进行操作,对性能没有什么要求

具体需要的线程数要进行测试才知道,通过时间和空间来计算

最优

的线程数。



版权声明:本文为m0_51945115原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。