1.线程的概念
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Android系统中,当我们启动一个应用时,Zygote会为其孵化一个进程,并在之后创建一个线程-即主线程或UI线程,应用的所有操作都在主线程进行。因此,当我们在主线程进行耗时操作时,就会阻塞主线程,导致页面卡顿甚至ANR的出现。为解决这一问题就必须使用多线程开发。
2.简单线程创建
- Thread-通过继承Thread或new Thread()并重写run()方法
fun main(args: Array<String>){
Thread(){
run {
print("Thread")
}
}.start()
MyThread().start()
}
class MyThread :Thread(){
override fun run() {
super.run()
print("MyThread")
}
}
运行结果:

- Runnable-通过实行Runnable
fun main(args: Array<String>){
Thread(Runnable {
println("Runnable")
}).start()
Thread(MyRunnable()).start()
}
class MyRunnable :Runnable{
override fun run() {
println("MyRunnable")
}
}
运行结果:

3.线程的创建及执行-源码分析
通过上述两种方式创建线程,都会调用到init()方法。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
@Override
public void run() {
if (target != null) {//如果Runnable不为空
target.run();//则调用Runnable的run()方法
}
}
/**
* Initializes a Thread.
* 初始化线程
* @param g the Thread group //线程组
* 线程组表示一组线程。此外,线程组还可以包括其他线程组。线程组形成一个树,其中除了初始线程
* 组以外的每个线程组都有一个父线程。
* @param target the object whose run() method gets called //Runnable的实现类
* @param name the name of the new Thread //线程的名字
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* stackSize表示线程所需的堆栈大小,若为零表示该参数将被忽略。
*/
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
//这是一个native方法,返回对当前正在执行的线程对象的引用
//这里返回的是主线程,因为我们的线程是在主线程中创建的,所以主线程是父进程
Thread parent = currentThread();
if (g == null) { //如果线程组为空
g = parent.getThreadGroup();//获取主线程的线程组
}
//增加线程组中未启动线程的计数。
//虽然未启动的线程没有添加到线程组中,但是为了便于在未启动的情况下可以收集它们,
//必须对它们进行计数,以便在线程组中具有未启动线程的守护进程线程组不会被销毁。
g.addUnstarted();
this.group = g;//把线程组赋给新线程的线程组
this.target = target;//传递Runnable
this.priority = parent.getPriority();//获取线程的优先级
this.daemon = parent.isDaemon();//是否是守护线程
setName(name);//设置线程的名字
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;//存储指定的堆栈大小
tid = nextThreadID();//生成新线程的ID
}
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* 线程开始执行,Java虚拟机会调用Thread.run()方法
* 同一个线程不能反复调用该方法
*/
public synchronized void start() {
// Android-changed: throw if 'started' is true
if (threadStatus != 0 || started)//如果不是一个新线程或线程已经start()了
throw new IllegalThreadStateException();//则直接抛出异常
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
/**
* 通知线程组这个线程即将启动
* 这样就可以将它添加到线程组列表中,同时减少线程组的未启动计数。
* /
group.add(this);
started = false;//表示还未真正启动
try {
//通过native方法执行线程任务
nativeCreate(this, stackSize, daemon);
started = true;//表示已经真正启动
} finally {
try {
if (!started) {//启动失败
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
NOTE:同一个线程不能多次调用start()方法,上面源码已经分析了。简单示例:
fun main(args: Array<String>) {
val thread = MyThread()
thread.start()
thread.start()
}
运行结果:

4.线程的关闭
线程执行完后,一定要记得及时开闭。不然会空耗系统资源。
- stop()-强制线程停止执行(已过时)。某些情况会抛出SecurityException异常。
- suspend()-被弃用
- destroy()-被弃用
- interrupt()-虽然方法注释上写着中断线程,但是它只是表示一个中断位置标识符。简单示例:
fun main(args: Array<String>) {
MyThread().start()
}
class MyThread() : Thread() {
var num:Int =1
override fun run() {
super.run()
while (true) {
try {
println("interrupt()之前$num")
if (num == 5) {
println("中断")
interrupt()
}
println("----${num++}----")
Thread.sleep(1000)
} catch (e: Exception) {
//interrupt()
}
}
}
}

class MyThread() : Thread() {
var num:Int =1
override fun run() {
super.run()
//如果调用了interrupt()方法,isInterrupted()为true,否则为false
//别调用interrupted(),该方法表示清除中断标志,并返回原状态
//当然你也可以使用volatale关键字修饰的变量来作标识符,
//但是如果是阻塞型任务,就找不到检查标识符的时机了
while (!isInterrupted) {
try {
if (num == 5){
interrupt()
}
println("----${num}----")
num++
Thread.sleep(1000)
} catch (e: Exception) {
interrupt()
}
}
}
}

NOTE:调用interrupt()方法后,线程会在合适的时候自己结束。
5.线程池
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。
5.1 线程池的创建
- Executors
- Executors.newFixedThreadPool(线程数)-线程数固定
- Executors.newFixedThreadPool(线程数,threadFactory)-线程数固
- Executors.newSingleThreadExecutor()-单一线程的线程池
- Executors.newCachedThreadPool()-无固定大小。创建一个线程池,该线程池根据需要创建新的线程,如果以前构建的线程可用就会复用它们。这种线程池通常会改善执行许多短期异步任务的程序的性能。如果没有现有线程可用,将创建一个新线程并将其添加到线程池池中。60秒未使用的线程将终止并从缓存中删除。因此,一个闲置时间足够长的池不会消耗任何资源。
简单示例:
- 执行单个任务
private val executorService = Executors.newFixedThreadPool(1)//创建持有一个线程的线程池
//同上
private val executorService2 = Executors.newFixedThreadPool(1,object :ThreadFactory{
override fun newThread(r: Runnable?): Thread {
return Thread(r)
}
})
private var num:Int = 1
fun main(args: Array<String>) {
//执行线程任务,无返回值
//executorService.execute {
// doTask("execute")
//}
//执行线程任务,返回Future对象。通过它可以判断任务的执行状态
val future= executorService.submit {
doTask("submit")
}
while (!future.isDone){
Thread.sleep(2000)
println("--------------")
}
}
private fun doTask(name:String){
while (!executorService.isShutdown){
if (num == 5){
executorService.shutdown()
}
println("---$name---$num----")
num++
Thread.sleep(1000)
}
}
运行结果:

- 执行多个任务
fun main(args: Array<String>) {
val executorService = Executors.newFixedThreadPool(3)//创建三个线程的线程池
//执行三个任务
val futures = executorService.invokeAll(arrayListOf(MyTask(),MyTask(),MyTask()))
futures.forEach {
println(it.get()+"${System.currentTimeMillis()}")
}
}
class MyTask:Callable<String>{//实现Callable接口
var num:Int =1
override fun call(): String {
while (num<7){
Thread.sleep(1000)
println("${Thread.currentThread().name}---->>$num}----")
num ++
}
return "${Thread.currentThread().name}--结束"
}
}
运行结果:

- ThreadPoolExecutor-上述两个方法都会调用到ThreadPoolExecutor
-------Executors类-------------
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
简单示例:
fun main(args: Array<String>) {
val threadPoolExecutor = ThreadPoolExecutor(
2,
2,
1,
TimeUnit.MINUTES,
LinkedBlockingDeque())
val futures = executorService.invokeAll(arrayListOf(MyTask(), MyTask()))
futures.forEach {
println(it.get() + "${System.currentTimeMillis()}")
}
}
运行结果:

6.线程池创建-源码分析
/**
* If false (default), core threads stay alive even when idle.
* If true, core threads use keepAliveTime to time out waiting
* for work.
* 默认是false,核心线程即使处于闲置状态也会存活在线程池中。如果为true,
* 则会响应keepAliveTime参数
*/
private volatile boolean allowCoreThreadTimeOut;
/**
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* 核心线程数。除非设置了allowCoreThreadTimeOut为ture,否则即使它们处于闲置状态,
* 也会存活在线程池中
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* 线程池允许的最大线程数
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* 线程的存活时间,即在等待任务时的时间
* @param unit the time unit for the {@code keepAliveTime} argument
* 时间类型
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* 工作队列
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
//上述三个构造器,最终都会调用到这里
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
上面分析还有两个参数没有说明
- BlockingQueue - 阻塞队列
- LinkedBlockingQueue - 是一个单向链表实现的阻塞队列。该队列按 FIFO(先进先出)排序元素,新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。如果不指定大小,默认值Integer.MAX_VALUE。
fun main(args: Array<String>) {
val threadPoolExecutor = ThreadPoolExecutor(
2,
2,
1,
TimeUnit.MINUTES,
LinkedBlockingDeque())
val futures = executorService.invokeAll(arrayListOf(MyTask(), MyTask(), MyTask(),MyTask()))
futures.forEach {
println(it.get() + "${System.currentTimeMillis()}")
}
}
运行结果:上面我们创建了核心线程为2,最大线程数为2的线程池,但是我们的任务有四个,所以先执行前面两个任务,第三、四个任务被阻塞,直到有线程已经执行完,再来执行第三、四个任务。

- ArrayBlockingQueue - 是一个用数组实现的有界阻塞队列,策略也是先进先出。
- SynchronousQueue - 没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
Executors.newCachedThreadPool()线程池用的就是该队列。
2.RejectedExecutionHandler - 拒绝策略,即任务在什么情况下不会被执行。通常都是使用默认的。