As everyone knows,线程池在java里就占有一定的分量,而这一点在Android上也有很广泛的应用。曾经无数次被问到过,什么是线程池,作用是什么?和线程的关系。是不是感觉头都大了,今天我就来整理一份关于线程池的一些内容。申明:此为个人学习总结part
1.什么是线程池?
理论上来说,线程池其实就是一个管理线程的地方。
2.为什么会有线程池这个东西的存在?
在以往的开发中我们需要子线程操作的时候,我们就随时随地的new一个Thread,或者弄一个Runnable来开启一个子线程,如果数量多了,以致于到处都是new的子线程,很多子线程空闲在那里,有些完成后又被GC,不停的GC,新建和闲置,造成浪费资源,还有可能会造成界面卡顿,对于线程的控制也很混乱。这个时候我们就可以用线程池,用线程池对项目的子线程进行管理和控制。
3.线程池的好处?
(a)线程的复用,重用已经创建好的线程,避免重复的创建和频繁的GC。
(b) 控制线程并发数,合理使用系统资源。
(c)可以有效控制线程的执行。定时执行,取消执行等操作。
探索线程池的用法
我们大概知道了线程池的作用和优点,接下来让我们了解一下它的一些类,一些方法,一些参数的意思。
1.ThreadPoolExecutor:它的构造方法有4种。这里挑最多参数的那组来说一下,因为最多的已经包含了其他的构造器里的参数。参数如下:
<a>corePoolSize 线程池中核心线程的数量
<b>maximumPoolSize 线程池中最大线程数量
<c>keepAliveTime 非核心线程的超时时长。在超过这个时间之后,非核心线程会被回收。若想在超时后回收核心线程,就设置ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则这个参数也表示这个核心线程的超时时长。
<c>unit 第三个参数的单位
<d>workQueue 线程池中的任务队列(和Handler的MessageQueue有点相似),这里面存储的都是那些通过ThreadPoolExecutor的execute提交的任务,这些任务是已经提交但是还未执行的那些任务。
<e>threadFactory 为线程池提供创建新线程的功能。
<f>handler
这个workQueue是一个BlockingQueue类型(阻塞队列).
而BlockingQueue又有如下类型:
(1)ArrayBlockingQueue:表示规定大小的BlockingQueue,存储在此的数据是先进先出(FIFO原则)
(2)LinkedBlockingQueue:表示大小不确定的BlockingQueue,LinkedBlockingQueue的构造函数里面有一个int值,给这个int一个值,那么代表这个LinkedBlockingQueue是有大小的。如果不给值,默认则是Interger.MAX_VALUE.
(3)priorityBlockingQueue:和LinkedBlockingQueue类似,但是它不是FIFO原则。它是由Comparator来决定存取顺序的(比如说按照年龄,身高排序)
(4)synchronousQueue:它是线程安全的BlockingQueue,它内部没有数据缓存空间。它的理解较为抽象,我们不能遍历读取其中的数据,而是存入和取出的时候会走SynchronousQueue过一下,所以说,在取走的时候这个数据此时才在队列里面存在一下。
你一定还想知道线程放到线程池之后是怎么去运行的呢?
前提都是在execute一个线程之后.......
(1)线程池的线程数<核心线程数,会立刻启用一个核心线程去执行
(2)线程池的线程数=核心线程数,而workQueue未满,则将新线程放入workQueue中等待执行。
(3)线程池的线程数=核心线程数,但是<非核心线程数,而workQueue已满,则开启一个非核心线程来执行任务。
(4)线程池的线程数>非核心线程数,则拒绝执行该任务。
接下来介绍几种线程池的类型
FixedThreadPool:核心线程数固定的线程池。
private void mFixedThreadPool(){
ExecutorService executorService = Executors.newFixedThreadPool(3);
for(int i=0;i<30;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
SystemClock.sleep(3000);
}
};
executorService.execute(runnable);
}
}
结果是:首先就在核心线程添加3个任务0,1,2的任务。然后再添加任务在workQueue,若核心线程有空了,就会来处理workQueue任务队列的任务。
singleThreadPool:核心线程数只有1条。但使用它可以避免去处理线程同步问题。
private void mSingleThreadPool(){
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i=0;i<30;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
SystemClock.sleep(3000);
}
};
executorService.execute(runnable);
}
}
结果是:首先就核心线程添加1个0的任务。然后再添加任务在workQueue里....
CachedThreadPool:根据程序运行自动调整线程池中的线程数量。最大为Interger.MAX_VALUE.所以它适合大量任务请求的时候。CachedThreadPool中没有新任务的时候,里面的线程会因为超时而被终止。
private void mCachedThreadPool(){
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=0;i<30;i++){
Runnable runnable=new Runnable() {
@Override
public void run() {
}
};
executorService.execute(runnable);
SystemClock.sleep(3000);//每隔3s在添加新任务。以致于之前的线程又空闲了,所以执行任务的应该都是那一个线程。
}
}
ScheduledThreadPool:具有定时定期执行任务功能的线程池。核心线程数是固定的,但非核心线程数是无穷大的,所以在非核心线程闲置的时候会立即被回收。
(1)延迟启动任务
//延迟启动任务
private void mDelayScheduledThreadPool(){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
Runnable runnable=new Runnable() {
@Override
public void run() {
}
};
executorService.schedule(runnable,2, TimeUnit.SECONDS);
}
(2)延迟定时执行任务
//延迟定时执行任务
private void mTimeScheduledThreadPool(){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
Runnable runnable=new Runnable() {
@Override
public void run() {
}
};
executorService.scheduleAtFixedRate(runnable,1,1,TimeUnit.SECONDS);
}
延时1s后,每隔1s执行一个新任务。
(3)延迟执行任务
//延迟执行任务
private void mTime2ScheduledThreadPool(){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
Runnable runnable=new Runnable() {
@Override
public void run() {
}
};
//第一次延迟1s,以后每次也延迟一秒执行
executorService.scheduleWithFixedDelay(runnable,1,1,TimeUnit.SECONDS);
}
在线程池里面还有很多其他的比较常用的方法。
submit:一般情况我们用execute来提交任务,但是有的时候我们还会用到submit,因为submit有返回值。
如下所示,callable接口实现异步任务,在call方法中来执行异步任务,那么突然要问一句,返回值是什么呢?你一定会有的懵逼,酱酱酱~其实返回值就是该任务的返回值。
你是不是要问我Future是什么?哎呀,其实Future就是返回结果,返回他的isDone属性表示异步任务执行成功。
private void mSubmint(){
List<Future<String>> futures=new ArrayList<>();
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
for(int i=0;i<10;i++){
Future<String> taskFuture=threadPoolExecutor.submit(new MyTask(i));
futures.add(taskFuture);
}
//遍历所有任务的结果
for(Future<String> future:futures){
try {
Log.i("mylog","future="+future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyTask implements Callable<String>{
private int taskId;
public MyTask(int taskId) {
this.taskId = taskId;
}
@Override
public String call() throws Exception {
SystemClock.sleep(1000);
//返回每一个任务的执行结果
return "TaskName="+Thread.currentThread().getName()+"//TaskId="+taskId;
}
}
除了使用submit可以让执行的任务有返回值之外,还有一种方式。那就是-----自定义线程池。
自定义线程池,那么那就是自定义ThreadPoolExecutor.
//自定义线程池
private void customThreadPool(View view){
MyThreadPool myThreadPool=new MyThreadPool(3,5,2000,TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
for(int i=0;i<10;i++){
final int finalI=i;
Runnable runnable=new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
Log.i("myLog","finalId="+finalI);
}
};
myThreadPool.execute(runnable);
}
}
class MyThreadPool extends ThreadPoolExecutor{
public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
Log.i("myLog","开始执行任务");
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Log.i("myLog","任务执行结束");
}
@Override
protected void terminated() {
super.terminated();
Log.i("myLog","线程池关闭");
}
}