线程间通信原理及Android多线程
线程间交互
-
一个线程启动别的线程:new Thread().start()、Executor.execute() 等
-
一个线程终结另一个线程
-
Thread.stop() 可以立即停止线程,现在已废弃,不推荐使用
Thread thread = new Thread(){ @Override public void run() { for (int i = 0; i < 100000; i++) { System.out.println(i); } } }; thread.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //不管线程中的打印是否完成,调用stop()后立即停止 thread.stop();
-
Thread.interrupt() 温和式终结:不立即、不强制
-
interrupted() 和 isInterrupted():检查(和重置)中断状态
Thread.interrupted()在检查过后会重置中断状态,isInterrupted()不会重置
-
InterruptedException:如果在线程「等待」时中断,或者在中断状态「等待」,直接结束等待过程(因为等待过程什什么也不会做,而 interrupt() 的目的是让线程做完收尾工作后尽快终结,所以要跳过等待过程)。InterruptedException也会重置中断状态,所以在sleep结束等待,进入到InterruptedException也要保证interrupt可以执行
Thread thread = new Thread(){ @Override public void run() { for (int i = 0; i < 100000; i++) { if (Thread.interrupted()){ return; } try { //如果线程在sleep等待,直接结束等待跳到catch中的InterruptedException Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); //InterruptedException也会重置中断状态,所以这里也需要return 保证中断可以执行 return; } System.out.println(i); } } }; thread.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt();
-
-
-
Object.wait() 和 Object.notify() / notifyAll()
- 在未达到目标时 wait()
- 用 while 循环检查
- 设置完成后 notifyAll()
- wait() 和 notify() / notifyAll() 都需要放在同步代码块里
Thread thread = new Thread(){ @Override public void run() { try { //如果线程在sleep等待,直接结束等待跳到catch中的InterruptedException Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } printString(); } }; thread.start(); Thread thread2 = new Thread(){ @Override public void run() { try { //如果线程在sleep等待,直接结束等待跳到catch中的InterruptedException Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } initString(); } }; thread2.start(); private synchronized void initString(){ word = "hello"; notifyAll(); } private synchronized void printString(){ while (word == null){ try { //当word为空的时候,先释放锁让initString()获得锁进行赋值,然后再notifyAll()唤醒printString() wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(word); }
-
Thread.join():让另一个线程插在自己前面
通过Thread.join()可以让几个线程按顺序来执行,或者通过
Executors.newSingleThreadExecutor()
也可以实现线程顺序执行。Thread thread = new Thread(){ @Override public void run() { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } }; thread.start(); Thread thread2 = new Thread(){ @Override public void run() { try { //让thread先执行 thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; thread2.start();
-
Thread.yield():暂时让出自己的时间片给同优先级的线程
Android的Handler机制
- 本质:在某个指定的运行中的线程上执行代码
- 思路:在接受任务的线程上执行循环判断
- 基本实现:
Thread 里 while 循环检查
加上 Looper(优势在于自定义 Thread 的代码可以少写很多):
再加上 Handler(优势在于功能分拆,而且可以有多个 Handler)
- Java 的 Handler 机制:
HandlerThread:具体的线程
Looper:负责循环、条件判断和任务执行
Handler:负责任务的定制和线程间传递
推荐Handler详解文章:
http://yifeiyuan.me/blog/f77487d3.html
https://www.zhihu.com/question/34652589
AsyncTask
AsyncTask 的内存泄露
众所周知的原因:AsyncTask 持有外部 Activity 的引用
没提到的原因:执行中的线程不会被系统回收
Java 回收策略:没有被 GC Root 直接或间接持有引用的对象,会被回收
GC Root:
- 运行中的线程
- 静态对象
- 来自 native code 中的引用
所以:AsyncTask 的内存泄露,其他类型的线程方案(Thread、Executor、HandlerThread)一样都有,所以不要忽略它们,或者认为 AsyncTask 比别的方案更危险。并没有。就算是使用 AsyncTask,只要任务的时间不长(例如 10 秒之内),那就完全没必要做防止内存泄露的处理。
Service 和 IntentService
- Service:后台任务的活动空间。适用场景:音乐播放器等。
- IntentService:执行单个任务后自动关闭的 Service
Executor、AsyncTask、HandlerThead、IntentService 的选择
原则:哪个简单用哪个
- 能用 Executor 就用 Executor
- 需要用到「后台线程推送任务到 UI 线程」时,再考虑 AsyncTask 或者 Handler
- HandlerThread 的使用场景:原本它设计的使用场景是「在已经运行的指定线程上执行代码」,但现实开发中,除了主线程之外,几乎没有这种需求,因为 HandlerThread 和 Executor相⽐比在实际应用中并没什什么优势,反而用起来会麻烦一点。不过,这二者喜欢用谁就用谁吧。
- IntentService:首先,它是一个 Service;另外,它在处理理线程本身,没有比 Executor 有任何优势
关于 Executor 和 HandlerThread 的关闭
如果在界面组件里创建 Executor 或者 HandlerThread,记得要在关闭的时候(例如Activity.onDestroy() )关闭 Executor 和 HandlerThread。
@Override
protected void onDestroy() {
super.onDestroy();
executor.shutdown();
handlerThread.quit(); // 这个其实就是停止 Looper的循环
}