线程间通信原理及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:

  1. 运行中的线程
  2. 静态对象
  3. 来自 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的循环
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容