java/android线程池详解
一,简述线程池:
线程池是如何工作的:一系列任务出现后,根据自己的线程池安排任务进行。
如图:

线程池的好处:
- 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
- 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
- 能对线程进行简单的管理。并提供定时执行以及指定间隔循环执行等功能。
线程池的具体实现为ThreadPoolExeutor,其接口为Executor。
ThreadPoolExecutor提供了一系列的参数用于配置线程池。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
参数含义如下:
- corePoolSize :线程池核心线程数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态。其有一个allowCoreThreadTimeOut属性如果设置为true,那么核心线程池会有超时策略。超时的时长为第三个参数 keepAliveTime 。如果超时,核心线程会被终结。
- maxmumPoolSize: 线程池所能容忍的最大线程数,当活动线程数达到这个数值后,后续的新任务会被阻塞。
- keepAliveTime:非核心线程闲置时的超时时长,超过这个时长就会被非核心线程会被回收。这个参数如同第一个参数,如果设置相关属性后也会作用于核心线程。、
- unit:指定keepAliveTime的参数时间单位。这是一个枚举,常用的有MILLISECONDS(毫秒)、SECONDS(秒)等
- workQueue:线程池的任务队列,通过execute()方法(执行方法)提交的Runable对象会存储在这个参数中。
- threadFactory:线程工厂,为线程池提供创建新线程的功能。
其执行任务时大致遵顼如下规则:
- 如果线程达到核心线程数量,那么回直接启动一个核心线程。
- 线程未达到核心线程的数量,任务会被插入到任务队列(workQueue)排队。
- 如果任务队列已满导致步骤二无法插入到任务队列,那么开启一个非核心线程执行。
- 如果步骤三的线程数量达到线程池规定数目(maxmumPoolSize),那么拒绝执行此任务。
二,线程池的相关类结构解析
看一下线程池的类结构图:

说以下这几点:
这是一个抽象工厂模式!
Executor和Executors的区别:
Executor仅仅是一个接口,其只有一个方法,execute()方法(执行方法)。
public interface Executor {
void execute(Runnable command);
}
而Executors是一个没有继承任何类的方法,其作为一个工厂方法类,用于创建形形色色的线程池等对象。如:FixedThreadPool、defaultThreadFactory(下面会具体讲),而这些具体的线程池方案(FixedThreadPool)亦或者自定义的线程池方案(ThreadPoolExeutor)都是来自ExecutorService接口,这个接口也是继承于Executor接口。通过类结构图可以很清楚的看到。
如,Executors生产出ExecutorService对象,源码如下
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
说到工厂模式,ThreadPoolExeutor的参数threadFactory是一个接口,仅有newThread方法,代码如下
public interface ThreadFactory {
Thread newThread(Runnable r);
}
其作用和executor接口类似,体现面向接口编程思想。其中例如如下两个方法,是工厂方法类Executors所生成的ThreadFactory对象。
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
public static ThreadFactory privilegedThreadFactory() {
return new PrivilegedThreadFactory();
}
defaultThreadFactory是这样创建线程的,如果要自定义不妨参照一下。
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
三 具体线程池的使用
说了那么多,要具体看看系统提供的线程池,主要是这四个:
- FixedTreadPool. fixed有稳固的含义,通过工厂方法类Executors的newFixedThreadPool方法创建。它是线程数量固定的线程池,仅有核心线程且没有超时策略,所以线程不会被回收。这意味着它能够快速的响应外界请求。
例子:这里设置了4个runnable和2个核心线程。
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.d("hello","sleep前");
SystemClock.sleep(3000);
Log.d("hello", "sleep后");
}
};
......
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(runnable);
executorService.execute(runable1);
executorService.execute(runable2);
executorService.execute(runable3);
输出如下,
06-24 15:49:47.962 12462-12497/? D/hello: sleep1前
06-24 15:49:47.962 12462-12496/? D/hello: sleep前
06-24 15:49:50.962 12462-12497/com.example.hang.myapplication D/hello: sleep1后
06-24 15:49:50.962 12462-12497/com.example.hang.myapplication D/hello: sleep2前
06-24 15:49:50.972 12462-12496/com.example.hang.myapplication D/hello: sleep后
06-24 15:49:50.972 12462-12496/com.example.hang.myapplication D/hello: sleep3前
06-24 15:49:53.962 12462-12497/com.example.hang.myapplication D/hello: sleep2后
06-24 15:49:53.972 12462-12496/com.example.hang.myapplication D/hello: sleep3后
可以看到,每次仅有两个线程去执行,当其中一个执行完毕后才轮到下一个。
当 核心线程数量改为 4 时,则会一次性执行完这4个runnable。
- CachedThreadPool.它是一种线程数量不定的线程池,只有非核心线程,可以简单理解为最大线程数是无限大的。CachedThreadPool的任务队列相当于一个空集合,这导致任何任务都会被立即执行,比较适合做一些大量的耗时较少的任务。
例子,修改为将上述的 ExecutorService executorService = Executors.newFixedThreadPool(2);替换为
ExecutorService executorService = Executors.newCachedThreadPool();,输出如下,一次性执行4个线程,且线程顺序不定。
06-24 15:53:08.582 16801-16873/com.example.hang.myapplication D/hello: sleep2前
06-24 15:53:08.582 16801-16872/com.example.hang.myapplication D/hello: sleep1前
06-24 15:53:08.582 16801-16871/com.example.hang.myapplication D/hello: sleep前
06-24 15:53:08.582 16801-16875/com.example.hang.myapplication D/hello: sleep3前
06-24 15:53:11.582 16801-16873/com.example.hang.myapplication D/hello: sleep2后
06-24 15:53:11.582 16801-16872/com.example.hang.myapplication D/hello: sleep1后
06-24 15:53:11.582 16801-16871/com.example.hang.myapplication D/hello: sleep后
06-24 15:53:11.582 16801-16875/com.example.hang.myapplication D/hello: sleep3后
- ScheduledThreadPool.Scheduled有 “预定的” 意思。核心线程池的数量书固定的且非核心线程池是没有限制的,非核心线程池被闲置时会被立即回收。主要用于执行定时任务和具有固定周期的重复任务。
例子:这里是延迟2000ms后开始执行
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.schedule(runnable,2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable1, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable2, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable3, 2000, TimeUnit.MILLISECONDS);
例子:延迟10ms后开始每1000ms执行一次runnable。
scheduledExecutorService.scheduleAtFixedRate(runnable,10,1000,TimeUnit.MILLISECONDS);
- SingleThreadExecutor.只有一个核心线程,所以确保所有的任务都是在一个线程里顺序执行。把所有的任务都放到一个线程,这样有一个好处是不需要处理线程同步问题。
这里就不做例子了,就是4个排着队依次执行,且不会乱序。
06-24 16:05:02.782 32057-32125/com.example.hang.myapplication D/hello: sleep前
06-24 16:05:05.782 32057-32125/com.example.hang.myapplication D/hello: sleep后
06-24 16:05:05.782 32057-32125/com.example.hang.myapplication D/hello: sleep1前
06-24 16:05:08.782 32057-32125/com.example.hang.myapplication D/hello: sleep1后
06-24 16:05:08.782 32057-32125/com.example.hang.myapplication D/hello: sleep2前
06-24 16:05:11.782 32057-32125/com.example.hang.myapplication D/hello: sleep2后
06-24 16:05:11.782 32057-32125/com.example.hang.myapplication D/hello: sleep3前
06-24 16:05:14.782 32057-32125/com.example.hang.myapplication D/hello: sleep3后
java/android线程池详解的更多相关文章
- java - jdk线程池详解
线程池参数详解 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUni ...
- Java多线程-----线程池详解
1. 线程池的实现原理 提交一个任务到线程池中,线程池的处理流程如下: 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务.如果 ...
- Java线程池详解(二)
一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉 ...
- 三、VIP课程:并发编程专题->01-并发编程之Executor线程池详解
01-并发编程之Executor线程池详解 线程:什么是线程&多线程 线程:线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系 ...
- nginx源码分析线程池详解
nginx源码分析线程池详解 一.前言 nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,n ...
- Java 并发编程 | 线程池详解
原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...
- Java线程池详解
一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...
- Java多线程之线程池详解
前言 在认识线程池之前,我们需要使用线程就去创建一个线程,但是我们会发现有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因 ...
- Java线程池详解(一)
一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...
随机推荐
- python 对数函数
from math import logfrom math import e print e #自然对数print log(e) #log函数默认是以e为底print log(100,10) #以10 ...
- 清除Xcode缓存和存档文件
XCode4.2 finder中找到 /Users/Library/Developer/Xcode (注:Library资源库是隐藏的文件夹) 里面有DerivedData和Snaps ...
- Linux makefile教程之书写命令四[转]
书写命令———— 每 条规则中的命令和操作系统Shell的命令行是一致的.make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟 在依赖规则后面的分号后的.在命令 ...
- SQL0668N 由于表 "db2inst1.test" 上的原因代码 "3",所以不允许操作(解因为LOAD引起的LOAD暂挂状态锁)
DB2解因为LOAD引起的LOAD暂挂状态锁 一般解锁命名是,SET INTEGRITY FOR temp_test IMMEDIATE CHECKED 但是load暂挂状态是解不了的,可以l ...
- sqlplus常用命令
原文 sqlplus常用命令 desc 表名 显示表的结构 show user 显示当前连接用户 show error 显示错误 sho ...
- Windows Server 2012 R2 设置 smtp 服务器
Windows Server 2012/2012 R2:安装和配置 SMTP 服务器 安装 SMTP 服务器 以下是安装 SMTP 服务器功能的步骤: 打开“服务器管理器”:单击键盘上的 Window ...
- Drupal 7.23:函数module_invoke_all()注释
/** * Invokes a hook in all enabled modules that implement it. * * All arguments are passed by value ...
- 【转】linux之e2label命令
转自:http://www.th7.cn/system/lin/201311/46743.shtml 前言 e2label命令,用于获取或设置ext2.ext3文件系统对应的分区的卷标. 卷标:简单来 ...
- DOM笔记(六):怎么进行JQuery扩展?
一.全局函数的扩展 全局函数是将独立的函数添加到JQuery的命名空间中区.在使用的时候,可以通过$.fucnName(param)或者jQuery.funcName(param)方式进行调用. 1. ...
- IOS平台设计规范
一.UI的控件概述: 1.框架UI的元素分为4类: A:栏:状态栏目和导航栏的结合体; B:内容视图:应用显示的内容信息,包括相关的交互行为,例如滚屏.插入.删除等操作进行排序; C:控制元素:产品行 ...