Android之多线程,包括线程、Java同步问题、阻塞队列、线程池、AsyncTask、HandlerThread、IntentService等内容。
本文是我一点点归纳总结的干货,但是难免有疏忽和遗漏,希望不吝赐教。
转载请注明链接:https://blog.csdn.net/feather_wch/article/details/79132183
有帮助的话请点个赞!万分感谢!
Android多线程(88题)
版本号: 2018/9/18(10:10)
文章目录
问题汇总
线程(13)
1、什么是进程
- 系统分配资源的最小单位
- 进程就是程序运行的实体
2、什么是线程
- 系统调度的最小单位
- 一个进程中可以包含多个线程
- 线程拥有各自的计数器、堆栈和局部变量等属性,能够访问共享的内存变量
3、线程的好处
- 比进程的资源消耗要小,效率要更高
- 多线程的并发性能减少程序的响应时间
- 多线程能简化程序的结构,使程序便于理解和维护
4、线程的状态有哪些?
状态 | 解释 | 备注 |
---|---|---|
New | 新创建状态 | 线程已被创建,还未调用start,做一些准备工作 |
Runnable | 可运行状态 | start之后进入,Runnable线程可能在运行也可能没有在运行,取决于系统分配的时间 |
Blocked | 阻塞状态 | 线程被锁阻塞,暂时不活动 |
Waiting | 等待状态 | 线程不运行任何代码,消耗最少的资源,直至调度器激活该线程 |
Timed Waiting | 超时等待状态 | 与Waiting 不同在于,可以在指定时间内返回 |
Terminated | 终止状态 | 当前线程执行完毕:可能是run运行结束,或者出现未能捕获的异常导致线程终止 |
5、线程如何从新建状态
进入可运行状态
?
Thread.start()
6、线程如何从可运行状态
到阻塞状态
- 线程在
请求锁
的时候会进入阻塞状态
- 线程一旦
得到锁
会返回到可运行状态
备注:
如下题目中的
Object.wait()
是指具体对象调用wait等方法---someObject.wait()
,Thread.join
是指具体线程调用该方法—childThread.join()
7、线程如何从可运行状态
切换到等待状态
- 进入等待:
Object.wait()---当前线程进入等待状态(当前线程需要已经获得过锁,且调用后会失去锁)、Thread.join()---父线程会等待子线程
- 退出:
Object.notify()和Object.notifyAll()
8、线程如何从可运行状态
切换到超时等待状态
- 进入:
Thread.sleep(long)、Thread.join(long)---让父线程等待子线程,子线程结束后父线程才继续执行、Object.wait(long)
- 退出:
Object.notify()、Object.notifyAll()
或者超时退出
9、线程如何从可运行状态
切换到终止状态
- 执行完毕
- 异常退出
10、Object.notify()、Object.notifyAll()之间的区别
Object.notify()
: 随机唤醒一个wait线程
,调用该方法后只有一个线程会由等待池
进入锁池
。
Object.notifyAll()
: 会将对象等待池
中的所有线程都进入锁池
,进行竞争。竞争到的线程会继续执行,在释放掉对象锁
后,锁池中的线程会继续开始竞争。(进入到锁池的线程,不会再进入等待池)
11、等待池和锁池是什么?
等待池
:线程调用对象的wait
方法后,会释放该对象的锁,然后进入到该对象的等待池中
。锁池
:线程想要获得对象的锁,但是此时锁已经被其他线程拥有,这些线程就会进入到该对象的锁池
中。
12、创建线程的三种方法
- 继承Thread,重写run方法
- 实现Runnable接口,并实现该接口的run方法
- 实现Callable接口(Executor框架中的内容,优势是能在任务结束后提供一个返回值,Runnable无法这样做),重写call方法。
- 推荐第二种
Runnable接口
的方法,因为继承Thread没有必要。
13、终止线程的两种方法
- 调用
Thread.interrupted()
设置中断标志位,并通过Thread.currentThread().isInterrupted()
检查标志位。缺点:被中断的线程不一定会终止- 在
run()
方法中设置boolean标志位(需要volatile
修饰为易变变量):条件满足时跳出循环,run运行结束,thread安全终止
14、实现Callable接口创建多线程
1-实现Callable接口,重写run方法。
2-使用FutureTask进行包装,并且执行
// 1、实现Callable接口,并用futureTask包装。
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// TODO sth,并返回结果。
Integer result = 1;
return result;
}
});
// 2、开启线程并且执行任务
Thread thread = new Thread(futureTask);
thread.start();
// 3、获取到线程执行的返回值
Integer result = futureTask.get();
同步(8)
1、重入锁是什么?(3)
- 重入锁ReentrantLock在Java SE 5.0引入
- 该锁支持一个线程对资源的重复加锁
- 一个线程在锁住锁对象后,其他任何线程都无法进入Lock语句
val mLock = ReentrantLock()
mLock.lock()
try {
//需要同步的操作
}finally {
mLock.unlock() //finally中进行解锁,避免死锁问题
}
2、可重入锁的用途?
阻塞队列
就是使用ReentrantLock
实现的。
3、条件对象/条件变量的作用(4)
- 用于管理那些获得锁却因部分条件不满足而无法正常工作的线程
- 可以通过
newCondition
获得锁lock
的条件变量(和ReentrantLock配合使用
)- 条件对象调用
await
方法,当前线程就会阻塞并且放弃该锁await
线程会进入阻塞状态
,直到另一个线程,调用同一条件对象的signalAll()
方法,之后等待的所有线程通过竞争条件去抢锁
//1. 可重入锁
val mLock = ReentrantLock()
mLock.lock()
//2. 条件变量
val condition = mLock.newCondition()
try {
while(条件不满足){
//3. await进入Block状态
condition.await()
}
//4. 条件满足方会进行后续操作
//...
//5. 操作完成后调用同一条件变量的signalAll去激活等待该条件的线程
condition.signalAll()
}finally {
mLock.unlock() //finally中进行解锁,避免死锁问题
}
4、synchronized同步方法(4)
Lock
和condition
提供了高度的锁定控制,然而大多数情况下不需要这样麻烦- 从Java 1.0开始,每个对象都有一个内部锁
- 当一个方法使用
synchronized
修饰,意味着线程必须获得内部锁,才能调用该方法
synchronized public void doSth() throws InterruptedException{
//1. 条件不满足,进入Block状态
while(条件不满足){
wait();
}
//2. 条件满足方会进行后续操作
//...
//3. 解除该锁,并通知所有阻塞的线程
notifyAll();
}
- 备注:Kotlin学的不深,暂时没找到Kotlin中同步的方法,就用Java实现
5、同步代码块的使用(1)和问题(2)
- java中可以通过给一个Object对象上锁,来使用代码块
- 同步代码块非常脆弱不推荐
- 一般实现同步,最好使用
java.util.concurrent
包下提供的类,例如阻塞队列
Object object = new Object();
synchronized (object){
//进行处理, 不推荐使用
}
6、synchronized方法和synchronized同步代码块的区别?
用synchronized修饰符
修饰的方法就是同步方法
synchronized代码块
需要一个对象锁
7、死锁是什么?
死锁
是指两个或者两个以上线程/进程
进行资源竞争时出现了阻塞现象,如果没有外力帮助,它们都将无法继续工作下去,此时系统处于死锁状态
。
8、可能出现死锁的场景
可重入锁ReentrantLock
在mLock.lock()
后如果出现异常,但是没有在try-catch
的finally
中没有执行mLock.unLock()
就会导致死锁。notify
比notifyAll
更容易出现死锁
Java中的volatile(13)
1、Java的堆内存是什么?(2)
- 堆内存用于存储
对象实例
- 堆内存被所有线程所共享: 会存在内存可见性问题
2、 Java中的局部变量、方法定义的参数是否会被线程所共享?
- 局部变量、方法定义的参数则不会在线程间共享:不存在内存可见性问题,也不会受到内存模型影响,
3、Java内存模型的作用
- Java内存模型控制线程之间的通信,
决定了
一个线程对共享内存的写入何时对另一个线程可见。定义了线程和主存之间的抽象关系:
- 线程之间的共享变量存储在
主存
中,每个线程都有一个私有的本地内存
,本地内存中存储了该该线程贡献变量的副本,本地内存是Java内存模型中的抽象概念
,实际上并不存在,覆盖了缓存、写缓存区、寄存器
等区域。
4、Java内存模型图