【Java】并发之线程池
2015-11-23 19:05
597 查看
先看一段我们熟悉的代码:
[java]
view plaincopy
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(new OneTask(i));
thread.start();
}
其中,OneTask为Runnable接口的实现类。
思考一个问题:当要异步执行大量任务时,比如1000个任务,通过这种方法,就要创建1000个线程。大量线程的创建、销毁,并发执行与线程调度,是一笔很大的性能开销。另外,这么多线程难以很好地管理,有多少任务正在执行、多少任务已经执行完成,无法得知。
线程池,可以很好地处理这些问题。从Java中实现线程池的类为ThreadPoolExecutor。上面的代码,修改为:
[java]
view plaincopy
LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(90);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 20, TimeUnit.SECONDS, workQueue);
for (int i = 0; i < 100; i++) {
threadPoolExecutor.execute(new OneTask(i));
}
ThreadPoolExecutor调用execute方法来“执行”任务(Runnable)实例,其实,说“提交”任务实例更为合理,因为不一定就能立即执行,只是提交到线程池,由线程池来调度执行。
来看一下ThreadPoolExecutor构造方法:
[java]
view plaincopy
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue);
corePoolSize:线程池核心线程数大小,所谓“核心线程”,即在线程池中必不可少的,即使该线程负责执行的任务执行完毕,也不可少销毁自己,必须留在池中,以等待执行新的任务。
maximumPoolSize:线程池最大线程数大小,要注意,最大线程数总会大于或等于核心线程数。为什么会有大于?因为,除了核心线程外,线程池还可以创建一些新线程加入池中,这些新线程不同于核心线程,它们完成任务,空闲一定的时间后便会销毁掉。而这个时间,由下面两个参数决定。
keepAliveTime:非核心线程空闲时保留在池中多长时间,除非有新任务要求它去执行,否则过了这段时间,该线程会销毁掉。TimeUnit为时间单位类,如传参TimeUnit.SECONDS,表示keepAliveTime的单位为秒。
workQueue:一个BlockingQueue类型的参数,我们暂且简单地理解为一个队列。线程池使用这个队列来装载等待的任务。上面代码中,使用了BlockingQueue接口的一个实现类LinkedBlockingQueue,它的构造方法有一个参数capacity,决定了线程池等待队列的容量,即最多可以允许多少个任务加入等待队列。
下面分析下线程池创建、提交任务、任务等待、执行任务的过程。
a. 当ThreadPoolExecutor刚构造时,池中是还没有线程的。
b. 当调用execute()方法提交任务时,线程池会创建一个线程,来执行该任务,此时,线程池当前的线程数(pool size)为1。
c. 继续提交任务,继续创建线程(假设前面的任务都还没有这么快执行完),池中线程数递增1,依此类推。当达到corePoolSize个时,就不再创建新线程,新提交的任务就会进入等待队列,等到corePoolSize个线程中,有某个线程执行完任务时,等待队列中的任务才得以进入线程池中执行。
d.(继续假设前面的任务都还没有这么快执行完)继续提交任务,继续加入等待队列,当超出了等待队列所能容纳的最大限度(BlockingQueue的capacity值)时。前面说的maximumPoolSize参数要发挥作用了。当设置的maximumPoolSize值大于corePoolSize值,则此时线程池会创建新线程来执行该任务,线程池中线程数递增1。当线程池中的线程数达到了maximumPoolSize个,就不能再创建了,否则会报RejectedExecutionException异常。
e.线程执行完一个任务后,会从等待队列中取出新任务,然后执行新任务,这个过程,等待队列的任务数会减少。当线程池也渐渐执行完所有任务后,corePoolSize个的x线程(核心线程)继续存活不会销毁。而那些非核心线程(最多maximumPoolSize-corePoolSize个),过了keepAliveTime时间后便会被销毁。
Executors类是一工厂类,提供了一些创建常用ThreadPoolExecutor的方法。贴一下这些方法的源码,会更容易理解。
a. 创建固定个数的线程池执行器:
[java]
view plaincopy
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
b. 创建单个线程的线程池执行器:
[java]
view plaincopy
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
单线程执行器,可保证按顺序地执行任务。
c.创建缓存的线程池执行器:
[java]
view plaincopy
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
缓存线程执行器,执行大量的耗时短的任务,能很好地改善性能。
@容新华技术博客 - http://blog.csdn.net/rongxinhua -
原创文章,转载请注明出处
[java]
view plaincopy
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(new OneTask(i));
thread.start();
}
其中,OneTask为Runnable接口的实现类。
思考一个问题:当要异步执行大量任务时,比如1000个任务,通过这种方法,就要创建1000个线程。大量线程的创建、销毁,并发执行与线程调度,是一笔很大的性能开销。另外,这么多线程难以很好地管理,有多少任务正在执行、多少任务已经执行完成,无法得知。
线程池,可以很好地处理这些问题。从Java中实现线程池的类为ThreadPoolExecutor。上面的代码,修改为:
[java]
view plaincopy
LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(90);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 20, TimeUnit.SECONDS, workQueue);
for (int i = 0; i < 100; i++) {
threadPoolExecutor.execute(new OneTask(i));
}
ThreadPoolExecutor调用execute方法来“执行”任务(Runnable)实例,其实,说“提交”任务实例更为合理,因为不一定就能立即执行,只是提交到线程池,由线程池来调度执行。
来看一下ThreadPoolExecutor构造方法:
[java]
view plaincopy
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue);
corePoolSize:线程池核心线程数大小,所谓“核心线程”,即在线程池中必不可少的,即使该线程负责执行的任务执行完毕,也不可少销毁自己,必须留在池中,以等待执行新的任务。
maximumPoolSize:线程池最大线程数大小,要注意,最大线程数总会大于或等于核心线程数。为什么会有大于?因为,除了核心线程外,线程池还可以创建一些新线程加入池中,这些新线程不同于核心线程,它们完成任务,空闲一定的时间后便会销毁掉。而这个时间,由下面两个参数决定。
keepAliveTime:非核心线程空闲时保留在池中多长时间,除非有新任务要求它去执行,否则过了这段时间,该线程会销毁掉。TimeUnit为时间单位类,如传参TimeUnit.SECONDS,表示keepAliveTime的单位为秒。
workQueue:一个BlockingQueue类型的参数,我们暂且简单地理解为一个队列。线程池使用这个队列来装载等待的任务。上面代码中,使用了BlockingQueue接口的一个实现类LinkedBlockingQueue,它的构造方法有一个参数capacity,决定了线程池等待队列的容量,即最多可以允许多少个任务加入等待队列。
下面分析下线程池创建、提交任务、任务等待、执行任务的过程。
a. 当ThreadPoolExecutor刚构造时,池中是还没有线程的。
b. 当调用execute()方法提交任务时,线程池会创建一个线程,来执行该任务,此时,线程池当前的线程数(pool size)为1。
c. 继续提交任务,继续创建线程(假设前面的任务都还没有这么快执行完),池中线程数递增1,依此类推。当达到corePoolSize个时,就不再创建新线程,新提交的任务就会进入等待队列,等到corePoolSize个线程中,有某个线程执行完任务时,等待队列中的任务才得以进入线程池中执行。
d.(继续假设前面的任务都还没有这么快执行完)继续提交任务,继续加入等待队列,当超出了等待队列所能容纳的最大限度(BlockingQueue的capacity值)时。前面说的maximumPoolSize参数要发挥作用了。当设置的maximumPoolSize值大于corePoolSize值,则此时线程池会创建新线程来执行该任务,线程池中线程数递增1。当线程池中的线程数达到了maximumPoolSize个,就不能再创建了,否则会报RejectedExecutionException异常。
e.线程执行完一个任务后,会从等待队列中取出新任务,然后执行新任务,这个过程,等待队列的任务数会减少。当线程池也渐渐执行完所有任务后,corePoolSize个的x线程(核心线程)继续存活不会销毁。而那些非核心线程(最多maximumPoolSize-corePoolSize个),过了keepAliveTime时间后便会被销毁。
Executors类是一工厂类,提供了一些创建常用ThreadPoolExecutor的方法。贴一下这些方法的源码,会更容易理解。
a. 创建固定个数的线程池执行器:
[java]
view plaincopy
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
b. 创建单个线程的线程池执行器:
[java]
view plaincopy
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
单线程执行器,可保证按顺序地执行任务。
c.创建缓存的线程池执行器:
[java]
view plaincopy
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
缓存线程执行器,执行大量的耗时短的任务,能很好地改善性能。
@容新华技术博客 - http://blog.csdn.net/rongxinhua -
原创文章,转载请注明出处
相关文章推荐
- Spring的核心技术(三)---Bean简介
- JDK并发工具类源码学习系列——LinkedBlockingQueue
- springMVC各个Filter的用法!
- java swt多线程问题的解决现场(一点一点耐心看)
- [转]用Ant实现Java项目的自动构建和部署
- 运用Java组件itext生成pdf
- java的标准输入输出(二)
- java中有垃圾回收GC, 为什么还要调用close
- 贴一篇我的Javadoc
- Java命名规范
- ajax Post JSON与SpringMVC
- Java基础笔记
- java 提取数据
- C#与java值传递区别解析
- c#与java的区别(简述1)
- java 每日习题(十)数据库数据转成json格式
- Java访问权限
- Hibernate学习及使用总结:121&12n&n2n
- java基本知识(三)
- Caused by: java.lang.UnsatisfiedLinkError: Couldn't load p7zip from loader dalvik.system.PathClassL