Java线程池的四种实现方法及实现原理及分析。
在阅读了《Android开发进阶,从小工到专家》的3.2.4.0~3.2.2.0 内容后启发很大,所以写了一篇博客总结一下关于线程池的部分内容。 0.首先什么是线程池?线程池就是创建多个线程并且进行管理的容器。(线程池是个容器,可以创建线程和管理线程,并且给线程分配任务)1.为什么要用线程池呢?我们都知道,在Java中创建一个线程其实是一个很简单的事情,只要new Thread就可以了,但是这样做
在阅读了《Android开发进阶,从小工到专家》的3.2.4.0~3.2.2.0 内容后启发很大,所以写了一篇博客总结一下关于线程池的部分内容。
0.首先什么是线程池?
线程池就是创建多个线程并且进行管理的容器。(线程池是个容器,可以创建线程和管理线程,并且给线程分配任务)
1.为什么要用线程池呢?
我们都知道,在Java中创建一个线程其实是一个很简单的事情,只要new Thread就可以了,但是这样做并不是一种很好的方式。那么为什么不好呢?
比如在一个项目里,全部都是用的new Thread的方式去启用线程,那么创建好Thread1,而1在运行的时候,创建了Thread2,等等等... 创建了10个线程的时候,
1,2,3都执行完毕了但是没被销毁,就可能导致无限制的新建线程,相互竞争,占用过多的系统资源,导致死锁以及OOM。
而且这些线程缺乏统一的管理的功能,也缺乏定期执行,定时执行,线程中断的功能。
说了这么多,那么线程池的好处是啥啊?
线程池的好处有以下3方面:
(1) 重用已经存在的线程,减少了线程的创建和销毁的开销。
(2)可有效控制最大并发的线程数,提高了系统资源的使用率避免很多竞争,避免了OOM啊 死锁啊等。
(3)可以提供定时和定期的执行方式,单线程,并发数量的控制等功能!
线程池可以使得对线程的管理更加方便,并且对高并发的控制尽在掌握。
2.四种Java线程池功能及分析
线程池都继承了ExecutorService的接口,所以他们都具有ExecutorService的生命周期方法:运行,关闭,终止;
因为继承了ExecutorService接口,所以它在被创建的时候就是处于运行状态,当线程没有任务执行时,就会进入关闭状态,只有调用了shutdown()的时候才是正式的终止了这个线程池。
java通过Executors工厂类提供我们的线程池一共有4种:
fixedThreadPool() //启动固定线程数的线程池
CachedThreadPool() //按需分配的线程池
ScheduledThreadPoolExecutor()//定时,定期执行任务的线程池
ThreadPoolExecutor()//指定线程数的线程池。
我们先来说参数最多的线程,方便下面对参数的理解,
ThreadPoolExecutor()
ThreadPoolExecutor是线程池的实现类之一,它主要是启动指定数量线程以及将任务添加到队列,并且将任务发送给空闲的线程。
我们来看一下拥有最多构造函数的线程池的构造函数吧:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
corePoolSize : 线程的核心线程数。
maximumPoolSize:线程允许的最大线程数。
keepAliveTime:当前线程池 线程总数大于核心线程数时,终止多余的空闲线程的时间。
Unit:keepAliveTime参数的时间单位
workQueue:队伍队列,如果线程池达到了核心线程数,并且其他线程都处于活动状态的时候,则将新任务放入此队列。
threadFactory:定制线程的创建过程
Handler:拒绝策略,当workQueue队满时,采取的措施。
ThreadPoolExecutor()的作用主要是让我们更灵活的使用线程池,这里不详细的举例了。
fixedThreadPool() //启动固定线程数的线程池
在Android中,由于系统资源有限,所以我们最常用的就是fixedThreadPool()。
fixedThreadPool(int size) 就只有一个参数,size,就是线程池中最大可创建多少个线程
我们来看一下该线程池的实现过程
在实现上跟ThreadPoolExecutor类似fixedThreadPool简化了实现过程,把corePoolSize和maximumpoolSize的值都设为传入的size,并且设置keepAliveTime为0ms,然后采用的是LinkedBlockingQueue队列,这个队列是链式结构,所以是无边界的。可以容纳无数个任务。
总结:比如我创建3个线程的fixedThreadPool ,当3个都为活跃的时候,后面的任务会被加入无边界的链式队列(LinkedBlockingQueue),有空闲,就执行任务。
使用过程:
private static void fixedThreadPool(int size) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(size);
// ExecutorService executorService2 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
Future<Integer> task = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("执行线程" + Thread.currentThread().getName());
return fibc(40);
}
});
System.out.println("第"+i+"次计算,结果:"+task.get());
}
结果:
CachedThreadPool() //按需分配的线程池
对比fixedThreadPool来说 CachedThreadPool就要更快一些,为什么快呢?
我们来看一下CachedThreadPool的实现源码:
可以看到,这里的 maximumPoolSize:线程允许的最大线程数。 为MAX_VALUE(无限大),
也就是说,只要有任务并且其他线程都在活跃态,就会开启一个新的线程 (因为没有上限)
而当有空闲的线程的时候,就会去调用空闲线程执行任务。
使用过程:
private static void newCacheThreadPool(){
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
Future task = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"---->"+fibc(20));
}
});
}
}
结果:
ScheduledThreadPoolExecutor() 定时定期的线程池
创建ScheduledThreadPoolExecutor很简单,只要传入corePoolSize()线程的核心线程数。就可以开启这个线程池
然后我们需要使用它的定时任务,就需要实现他的scheduleAtFixedRate方法 这个方法有4个参数:
第一个为要执行的任务。
第二个为每次任务执行的延迟,比如传入1,就会每隔1秒执行一次。
第三个为执行的周期
第四个为第二个参数的时间单位。
*无论实现几个scheduleAtFixedRate方法,他们都互不干扰。
实现如下
private static void newScheduledThreadPool(){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----->" + fibc(40));
}
},5,3,TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----->" + fibc(5));
}
},1,1,TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----->" + fibc(1));
}
},1,2,TimeUnit.SECONDS);
}
我这里实现了3个scheduleAtFixedRate方法,
第一个为5s一次,3个周期执行一次
第二个为1s一次,1个周期执行一次
第三个为1s一次,2个周期执行一次
结果如下:
总结:线程池的使用,可以更规范的管理和创建销毁线程,也可以更多样化的去使用线程,减小我们的系统开支。
更多推荐
所有评论(0)