Java多線程-完成Android開發中的某些需求

一 需求

子線程中開啓一個主線程去獲取某些數據,此時子線程要處理等待狀態,獲取成功以後繼續執行子線程中以後的代碼.java

  • 問題:
    當開啓主線程去獲取數據的時候,子線程的代碼也會順序去執行,這樣並不能等到主線程有結果的時候再去執行子線程中的代碼.
  • 分析:
    先來分析一下,當在線程A中開啓另一個線程B的時候,線程A中的代碼仍是順序執行線程B的代碼也會執行.這樣的話線程A中須要線程B中返回參數的方法就沒辦法正確執行.
  • 解決:
    這裏就用到了線程的wiat() notify() 和 synchronized 關鍵字,先來看下解決方法.
    final  Object obj = new Object();
         new Thread(){
              @Override
              public void run() {
                  super.run();
                  //經過Handler建立一個主線程.
                  new Handler(getMainLooper()).post(new Runnable() {
                      @Override
                      public void run() {
                          synchronized (obj) {
                          //模擬主線程耗時操做
                              try {
                                  Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                              str = "WangChao";
                              Log.e("CHAO", "run 2" + str);
                              obj.notify();
                          }
                      }
                  });
                  Log.e("CHAO","run 1");
                  synchronized (obj) {
                      try {
                          obj.wait();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      //子線程須要主線程的返回參數
                      if (str != null) {
                          Log.e("CHAO", "run 3" + str);
                      }
                  }
              }
          }.start();複製代碼
    運行可知先運行的是run 1 ,以後是主線程中的run 2 ,而後是子線程中的 run 3.這樣就能夠達到咱們想要的結果.運行結果以下:
    com.example.wang.threaddemo E/CHAO: run 1
    com.example.wang.threaddemo E/CHAO: run 2WangChao
    com.example.wang.threaddemo E/CHAO: run 3WangChao複製代碼

二 涉及的Java線程的知識點

  • synchronized關鍵字的用法
    被synchronized修飾的方法稱爲同步方法,默認的鎖是當前對象.所謂同步方法是若是有一個線程正在訪問該方法的時候,其餘的方法調用到該方法的時候將進入等待的狀態.當有多個同步方法的時候,一旦一個線程調用了其中的一個同步方法,便對當前對象加鎖,其餘的線程調用擁有同對象鎖的其它同步方法時會處於等待的狀態,等以前的線程釋放掉對象鎖以後繼續執行.
    1.去修飾方法 : 對象鎖默認是當前對象 this .bash

    public synchronized void method2(){
              Log.e("CHAO", "method2: " + Thread.currentThread().getName());
      }複製代碼

    還能夠這樣寫:多線程

    public  void method2(){
                  synchronized (this) {
                      Log.e("CHAO", "method2: " + Thread.currentThread().getName());
                  }
              }複製代碼

    2.同步語句塊 : 能夠本身去設置加鎖的對象.ide

    synchronized (obj) {
                              try {
                                  Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                              str = "WangChao";
                              Log.e("CHAO", "2" + str);
                              obj.notify();
                          }複製代碼

    這裏須要注意的是synchronized (obj) 裏面的參數,也就是要給那個對象加鎖.這個對象必須是惟一的切多個線程都能拿到這個對象.(再使用Stirng最爲對象鎖的時候必定要注意String的從新賦值,一旦賦值改對象就改變了再也不是咱們以前加鎖的對象),一般能夠本身去建立一個final 的 Object類去加鎖,也可使用this,Class對象等.oop

    3.同步靜態方法啊: 加鎖對象是當前的Class對象.post

    public static synchronized void method1(){
              int count = 0;
              for (int i = 0; i < 120000; i++) {
                  count++;
              }
              Log.e("CHAO", "method1: " + Thread.currentThread().getName());
      }複製代碼

    同不得靜態方法再多線程中的調用和非靜態的是一致的.ui

  • wait()
    首先wait()是屬於Object類的方法,從源碼給出的解釋來看,wait()方法能夠作到以下幾點:
    (1)首先,調用了wait()以後會引發當前線程處於等待狀狀態。
    (2)其次,每一個線程必須持有該對象的monitor(監視)。若是在當前線程中調 用wait()方法以後,該線程就會釋放monitor的持有對象並讓本身處於等 待狀態。
    (3)若是想喚醒一個正在等待的線程,那麼須要開啓一個線程經過notify()或者notifyAll()方法去通知正在等待的線程獲取monitor對象。如此,該線程便可打破等待的狀態繼續執行代碼.this

    注意: wait()方法要使用在synchronized修飾的方法裏面要否則會報異常,而且是synchronized()加鎖的那個對象調用該方法.異常以下:spa

    java.lang.IllegalMonitorStateException: object not locked by thread before wait()複製代碼
  • notify() , nitifyAll()
    notify()是屬於Object類的方法.notify喚醒一個在這個對象上等待的線程監控。若是有任何線程在等待這個對象,其中一個線程被選擇被喚醒。這個選擇是任意的,而且發生在執行的自由裁量。一個線程在一個對象上等待經過調用wait()等方法來監視。nitifyAll喚醒全部的再等待中的線程.線程

    注意:nitify喚醒的線程會在該線程的wiat以後繼續執行,特別要注意的是調用wiat的同步語句塊的對象鎖要和nitify的同步語句塊的對象鎖是同一個,不然的話你是喚醒不了wait狀態的.

    final  Object obj = new Object();
         new Thread(){
              @Override
              public void run() {
                  super.run();
                  new Handler(getMainLooper()).post(new Runnable() {
                      @Override
                      public void run() {
                      //注意這個加鎖對象
                          synchronized (obj) {
                              try {
                                  Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                              str = "WangChao";
                              Log.e("CHAO", "2" + str);
                              //注意喚醒方法的調用者
                              obj.notify();
                          }
                      }
                  });
                  Log.e("CHAO","run"+1);
                  //注意這個加鎖對象
                  synchronized (obj) {
                      try {
                      //注意等待方法的調用者
                          obj.wait();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      if (str != null) {
                          Log.e("CHAO", "3" + str);
                      }
                  }
              }
          }.start();複製代碼
  • sleep()
    sleep()方法來自於Thread類,從源碼給出的解釋來看,sleep()方法能夠作到以下幾點:
    (1)首先,調用sleep()以後,會引發當前執行的線程進入暫時中斷狀態,也即睡眠狀態。
    (2)其次,雖然當前線程進入了睡眠狀態,可是依然持有monitor對象。
    (3)在中斷完成以後,自動進入喚醒狀態從而繼續執行代碼

總結

(1)在線程的運行過程當中,調用該線程持有monitor對象的wait()方法時,該線程首先會進入等待狀態,並將本身持有的monitor對象釋放。
(2)若是一個線程正處於等待狀態時,那麼喚醒它的辦法就是開啓一個新的線程,經過notify()或者notifyAll()的方式去喚醒。固然,須要注意的一點就是,必須是同一個monitor對象。
(3)sleep()方法雖然會使線程中斷,可是不會將本身的monitor對象釋放,在中斷結束後,依然可以保持代碼繼續執行。

歡迎你們的點評和補充,喜歡的話就給個贊👍支持下吧~

相關文章
相關標籤/搜索