欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java深入学习(3):线程池原理

程序员文章站 2022-04-10 22:13:24
线程池的特点: 降低资源:通过重复利用已创建的线程降低线程创建和销毁的损耗 提高效率:当任务到底时,不需要等待,立即执行 方便管理:统一分配,调优和监控等 线程池的创建方式: 1.CachedThreadPool:创建一个可缓存线程池,灵活回收空闲线程 打印后可以发现:同一个线程有被再次利用,线程池 ......

线程池的特点:

降低资源:通过重复利用已创建的线程降低线程创建和销毁的损耗

提高效率:当任务到底时,不需要等待,立即执行

方便管理:统一分配,调优和监控等

 

线程池的创建方式:

1.cachedthreadpool:创建一个可缓存线程池,灵活回收空闲线程

public class threadpooldemo {
    public static void main(string[] args) {
        executorservice newcachedthreadpool = executors.newcachedthreadpool();
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            newcachedthreadpool.execute(new runnable() {
                @override
                public void run() {
                    system.out.println(thread.currentthread().getname() + "->" + temp);
                }
            });
        }
    }
}

打印后可以发现:同一个线程有被再次利用,线程池理论大小是无限的

 

2.fixedthreadpool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

        executorservice newfixedthreadpool = executors.newfixedthreadpool(5);

其他代码不变,打印发现五个线程都在复用

 

3.scheduledthreadpool:支持定时性地执行任务

public class threadpooldemo {
    public static void main(string[] args) {
        scheduledexecutorservice newscheduledthreadpool = executors.newscheduledthreadpool(5);
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            newscheduledthreadpool.schedule(new runnable() {
                @override
                public void run() {
                    system.out.println(thread.currentthread().getname() + "->" + temp);
                }
            }, 3, timeunit.seconds);
        }
    }
}

观察打印:等待3s后打印

 

4.singlethreadexecutor:单线程

        executorservice newsinglethreadexecutor = executors.newsinglethreadexecutor();

打印发现只调用了一个线程

 

线程池的原理:

四种方式都走了threadpoolexecutor的构造方法:

    public static executorservice newsinglethreadexecutor() {
        return new finalizabledelegatedexecutorservice
            (new threadpoolexecutor(1, 1,
                                    0l, timeunit.milliseconds,
                                    new linkedblockingqueue<runnable>()));
    }
    public threadpoolexecutor(int corepoolsize,
                              int maximumpoolsize,
                              long keepalivetime,
                              timeunit unit,
                              blockingqueue<runnable> workqueue) {
        this(corepoolsize, maximumpoolsize, keepalivetime, unit, workqueue,
             executors.defaultthreadfactory(), defaulthandler);
    }

corepoolsize:最大核心线程数:实际运用线程数

maximumpoolsize:最大线程数:线程池最多创建线程数

如果当前线程池中的线程数目小于corepoolsize,则每来一个任务,就会创建一个线程去执行这个任务

如果当前线程池中的线程数目>=corepoolsize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;

如果队列已经满了,则在总线程数不大于maximumpoolsize的前提下,则创建新的线程;

如果当前线程池中的线程数目达到maximumpoolsize,则会采取任务拒绝策略进行处理;

如果线程池中的线程数量大于 corepoolsize时,如果某线程空闲时间超过keepalivetime,线程将被终止,直至线程池中的线程数目不大于corepoolsize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepalivetime,线程也会被终止。

Java深入学习(3):线程池原理

 

 

写一段代码来理解:

public class mythreadpool {
    public static void main(string[] args) {
        threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(
                //核心线程数为1
                1,
                //最大线程数为2
                2,
                //保持存活时间0毫秒,意思是用完不回收
                0l, timeunit.milliseconds,
                //阻塞队列
                new linkedblockingqueue<runnable>(3));
        threadpoolexecutor.execute(new taskthread("task1"));
        threadpoolexecutor.execute(new taskthread("task2"));
        threadpoolexecutor.execute(new taskthread("task3"));
    }
}

class taskthread implements runnable {
    private string threadname;

    taskthread(string threadname) {
        this.threadname = threadname;
    }

    @override
    public void run() {
        system.out.println(threadname);
    }
}

提交task1时候,创建一个线程直接执行,此时核心线程数等于存在线程数

提交task2时候,存放在队列缓存,此时存在线程数等于最大线程数

提交task3时候,也存放在队列中缓存,而我阻塞队列大小是3,现在只存放了两个,所以不会报错

理论上,提交到task5都不会报错,最大线程数2+队列大小3=5;但是提交task6一定会报错

 

另外一点:如果提交到task4,打印线程名称:发现只调用了线程1,第2个线程没有调用过

因为:task1提交时候,task2,task3,task4存放在队列中,恰好到达队列最大值,所以不创建新线程,而是线程1依次执行这三个任务

 

合理配置线程池:

原则:

cpu密集:任务需要大量的运算,但没有阻塞的情况,cpu可以全速运行

io密集:任务需要大量的io操作,产生阻塞

 

cpu密集型线程数越少越好,最好配置为cpu核心数量

io密集型线程数要尽量多,最好配置为cpu核心数量*2