天天看點

1.線程基礎

文章目錄

  • ​​部落格概述​​
  • ​​線程安全概念​​
  • ​​線程安全案例引入​​
  • ​​同步鎖關鍵字:synchronized​​
  • ​​多個線程多個鎖​​
  • ​​對象鎖的同步和異步​​

部落格概述

程式開發中一個不可避免的問題就是多線程并發通路,為了這種場景特意準備了一個專欄記錄多線程的具體知識與代碼。部落客寫部落格的思路不是長篇大論的寫文字,而是通過代碼去說明問題。通過代碼配合注釋,去了解某種技術。

線程安全概念

當多個線程通路某一個類或者方法時,這個類始終都能表現出正确的行為,那麼這個類就是線程安全的。

線程安全案例引入

import java.util.concurrent.atomic.AtomicInteger;
/**
 * 線程安全概念:當多個線程通路某一個類(對象或方法)時,這個對象始終都能表現出正确的行為,那麼這個類(對象或方法)就是線程安全的。
 * synchronized:可以在任意對象及方法上加鎖,而加鎖的這段代碼稱為"互斥區"或"臨界區"
 * @author alienware
 *
 */
public class MyThread extends Thread{
  
  private int count = 5 ;
  
  //synchronized加鎖
  public void run(){
    count--;
    System.out.println(this.currentThread().getName() + " count = "+ count);
  }
  
  public static void main(String[] args) {
    /**
     * 分析:當多個線程通路myThread的run方法時,以排隊的方式進行處理(這裡排對是按照CPU配置設定的先後順序而定的),
     *    一個線程想要執行synchronized修飾的方法裡的代碼:
     *    1 嘗試獲得鎖
     *    2 如果拿到鎖,執行synchronized代碼體内容;拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到為止,
     *       而且是多個線程同時去競争這把鎖。(也就是會有鎖競争的問題)
     */
    MyThread myThread = new MyThread();
    Thread t1 = new Thread(myThread,"t1");
    Thread t2 = new Thread(myThread,"t2");
    Thread t3 = new Thread(myThread,"t3");
    Thread t4 = new Thread(myThread,"t4");
    Thread t5 = new Thread(myThread,"t5");
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
  }
}      

該程式列印出來的代碼不會按照43210去執行,這就出現了線程問題。

同步鎖關鍵字:synchronized

可以在任意的對象與方法上加鎖,加鎖的這段代碼稱為互斥區,臨界區。需要注意的是,在android開發中,使用api19是無法在對象上進行加鎖的,隻能對方法加鎖。采取的技巧可以是私有化靜态變量,然後對該變量提供公有靜态方法對外開放,使用sync關鍵字修飾,就可以保證某變量的線程安全問題。

對上面的例子隻要在run方法前面加上sync關鍵字就可以實作線程安全。使通路的線程排隊通路,但是效率會降低,而且會産生鎖競争的問題。這個鎖競争問題會讓cpu使用率瞬間激增。這就會導緻應用運作慢,甚至當機。

多個線程多個鎖

/**
 * 關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼(方法)當做鎖,
 * 是以代碼中哪個線程先執行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖(Lock),
 * 
 * 在靜态方法上加synchronized關鍵字,表示鎖定.class類,類一級别的鎖(獨占.class類)。
 * @author alienware
 *
 */
public class MultiThread {

  private int num = 0;
  
  /** static */
  public synchronized void printNum(String tag){
    try {
      
      if(tag.equals("a")){
        num = 100;
        System.out.println("tag a, set num over!");
        Thread.sleep(1000);
      } else {
        num = 200;
        System.out.println("tag b, set num over!");
      }
      
      System.out.println("tag " + tag + ", num = " + num);
      
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  
  //注意觀察run方法輸出順序
  public static void main(String[] args) {
    
    //倆個不同的對象
    final MultiThread m1 = new MultiThread();
    final MultiThread m2 = new MultiThread();
    
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        m1.printNum("a");
      }
    });
    
    Thread t2 = new Thread(new Runnable() {
      @Override 
      public void run() {
        m2.printNum("b");
      }
    });   
    
    t1.start();
    t2.start();
    
  }      

對象鎖的同步和異步

/**
 * 對象鎖的同步和異步問題
 *
 */
public class MyObject {

  public synchronized void method1(){
    try {
      System.out.println(Thread.currentThread().getName());
      Thread.sleep(4000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  
  /** synchronized */
  public void method2(){
      System.out.println(Thread.currentThread().getName());
  }
  
  public static void main(String[] args) {
    
    final MyObject mo = new MyObject();
    
    /**
     * 分析:
     * t1線程先持有object對象的Lock鎖,t2線程可以以異步的方式調用對象中的非synchronized修飾的方法
     * t1線程先持有object對象的Lock鎖,t2線程如果在這個時候調用對象中的同步(synchronized)方法則需等待,也就是同步
     */
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        mo.method1();
      }
    },"t1");
    
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        mo.method2();
      }
    },"t2");
    
    t1.start();
    t2.start();
    
  }
  
}      

繼續閱讀