博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java多线程(6)线程池
阅读量:6758 次
发布时间:2019-06-26

本文共 5233 字,大约阅读时间需要 17 分钟。

池的概念在java中也是常见,还有连接池、常量池等,池的作用也是类似的,对于对象、资源的重复利用,减小系统开销,提升运行效率。

线程池的主要功能:

1.减少创建和销毁线程的次数,提升运行性能,尤其是在大量异步任务时
2.可以更合理地管理线程,如:线程的运行数量,防止同一时间大量任务运行,导致系统崩溃

demo

先举个demo,看看使用线程池的区别,线程池:

AtomicLong al = new AtomicLong(0l);            Long s1 = System.currentTimeMillis();            ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 100000, 100, TimeUnit.SECONDS, new LinkedBlockingDeque(20000));            for(int i=0;i<20000;i++){                pool.execute(new Runnable() {                                        @Override                    public void run() {                        try {                            al.incrementAndGet();                        } catch (Exception e) {                            e.printStackTrace();                        }                    }                });            }            pool.shutdown();            pool.awaitTermination(1, TimeUnit.HOURS);            System.out.println("耗时:"+(System.currentTimeMillis()-s1));            System.out.println("AtomicLong="+al.get());

结果:

耗时:224AtomicLong=20000

非线程池:

AtomicLong al = new AtomicLong(0l);                        Long s1 = System.currentTimeMillis();            for(int i=0;i<20000;i++){                Thread t = new Thread(new Runnable() {                                        @Override                    public void run() {                        try {                            al.incrementAndGet();                        } catch (Exception e) {                            e.printStackTrace();                        }                    }                });                t.start();            }            while(true){                if(al.get()==20000){                    System.out.println("耗时:"+(System.currentTimeMillis()-s1));                    System.out.println("AtomicLong="+al.get());                    break;                }                Thread.sleep(1);            }

结果:

耗时:2972AtomicLong=20000

从耗时看2者相差了13倍,差距还是挺大的,可见有大量异步任务时使用线程池能够提升性能。

线程池的主要参数

public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue
workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {

线程池的主要参数有这6个:

corePoolSize:核心池的大小,核心池的线程不会被回收,没有任务就处于空闲状态。

maximumPoolSize:线程池最大允许的线程数,

keepAliveTime:当线程数超过corePoolSize时,但小于等于maximumPoolSize,会生成‘临时’线程,‘临时’线程执行完任务不会马上被回收,如果在keepAliveTime时间内,还没有新任务的话,才会被回收。

unit:keepAliveTime的单位。

workQueue:当前线程数超过corePoolSize大小时,新任务会处在等待状态,存在workQueue中。

threadFactory:生成新线程的工厂。

handler:当线程数超过workQueue的上限时,新线程会被拒绝,这个参数就是新线程被拒绝的方式。

其中corePoolSize和maximumPoolSize相对难理解,再详细说明:

1、池中线程数小于corePoolSize,新任务都不排队而是直接添加新线程

2、池中线程数大于等于corePoolSize,workQueue未满,首选将新任务加入workQueue而不是添加新线程

3、池中线程数大于等于corePoolSize,workQueue已满,但是线程数小于maximumPoolSize,添加新的线程来处理被添加的任务

4、池中线程数大于大于corePoolSize,workQueue已满,并且线程数大于等于maximumPoolSize,新任务被拒绝,使用handler处理被拒绝的任务

因此maximumPoolSize、corePoolSize不宜设置过大,否则会造成内存、cpu过载的问题,workQueue而尽量可以大一些。

Executors

虽然ThreadPoolExecutor使用很方便,但是建议大家使用jdk已经设定的几种线程池:

Executors.newCachedThreadPool()(无界线程池,可以进行线程自动回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和Executors.newSingleThreadExecutor()(单个后线程),它们满足大部分的场景需求。

1.newSingleThreadExecutor 单线程线程池

看下它的实现方式:

public static ExecutorService newSingleThreadExecutor() {        return new FinalizableDelegatedExecutorService            (new ThreadPoolExecutor(1, 1,                                    0L, TimeUnit.MILLISECONDS,                                    new LinkedBlockingQueue
())); }

把corePoolSize设为1,而workQueue大小设为无限大,因此永远只有一个线程在执行任务,新任务都在workQueue中等待。

2.newFixedThreadPool 固定大小线程池

public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue
()); }

和newSingleThreadExecutor 有些类似,只不过从单线程变成可以指定线程数量,workQueue依旧为无限。

3.newCachedThreadPool

public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue
()); }

newCachedThreadPool所有新任务都会被立即执行,corePoolSize 设置为0,maximumPoolSize设置为整数最大值,所有线程超过60秒未被使用,就会被销毁。

workQueue

当前线程数超过corePoolSize大小时,新任务会处在等待状态,存在workQueue中。workQueue的实现方式也就是任务的等待策略,分为3种:有限队列、无限队列、直接提交。

谈谈前2种,jdk使用了LinkedBlockingQueue而非ArrayBlockingQueue,有限队列的优势在于对资源和效率更定制化的配置,但是对比无限队列缺点也很明显:

1).增加开发难度。需要考虑到corePoolSize ,maximumPoolSize,workQueue,3个参数大小,既要使任务高效地执行,又要避免任务超量的问题,对于开发和测试的复杂度提高很多。

2).防止业务突刺。在互联网应用业务量暴增也是必须考虑的问题,在使用无限队列时,虽然执行可能慢一点,但是能保证执行到。使用有限队列,会出现任务拒绝的问题。
综上考虑,更建议使用无限队列,尤其是对于多线程不是很熟悉的朋友们。

拒绝策略

所谓拒绝策略之前也提到过了,任务太多,超过maximumPoolSize了怎么办?当然是接不下了,接不下那只有拒绝了。拒绝的时候可以指定拒绝策略,也就是一段处理程序。

决绝策略的父接口是RejectedExecutionHandler,JDK本身在ThreadPoolExecutor里给用户提供了四种拒绝策略,看一下:

1、AbortPolicy

直接抛出一个RejectedExecutionException,这也是JDK默认的拒绝策略

2、CallerRunsPolicy

尝试直接运行被拒绝的任务,如果线程池已经被关闭了,任务就被丢弃。

3、DiscardOldestPolicy

移除最晚的那个没有被处理的任务,然后执行被拒绝的任务。同样,如果线程池已经被关闭了,任务就被丢弃了

4、DiscardPolicy

悄悄地拒绝任务

转载地址:http://pwzeo.baihongyu.com/

你可能感兴趣的文章
Conversion to Dalvik format failed with error 1的又一种情形
查看>>
nodejs抓取数据二(列表解析)
查看>>
TextView中实现可点击链接的显示
查看>>
HAOI 树上操作
查看>>
深刻理解Python中的元类(metaclass)以及元类实现单例模式
查看>>
java随机生成n个不相同的整数
查看>>
DIV+CSS基础
查看>>
使用JS完成首页定时弹出广告图片
查看>>
codeforces 500c New Year Book Reading 【思维】
查看>>
Auto reloading enabled
查看>>
GitHub的使用方法
查看>>
AT3576 Popping Balls
查看>>
springboot入门_多数据源
查看>>
如果一个游戏上面加一个透明层,js能不能实现 点击透明层的任意点 而正常玩游戏...
查看>>
图的m着色问题
查看>>
oracle 查询char类型的数据
查看>>
Vue项目碰到"‘webpack-dev-server’不是内部或外部命令,也不是可运行的程序或批处理文件"报错...
查看>>
Android zxing扫描二维码 为什么有些机型扫描不出来或者很慢?
查看>>
SQLHelp sql数据库的DAL
查看>>
进阶第二课 Python内置函数(补)及自定义函数
查看>>