线程间通信原理及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的循环
}

你可能感兴趣的:(线程间通信原理及Android多线程)