1. 程式人生 > >5.並發編程-synchronized 細節說明

5.並發編程-synchronized 細節說明

dubbo read zed ext tro 當前 使用 current runnable

並發編程-synchronized 細節說明

1. synchronized-鎖重入 & 異常釋放鎖 說明

  • * 關鍵字synchronized 擁有鎖重入的功能,也就是在使用synchronized時,當一個線程得到一個對象的鎖後,再次請求此對象時可以再次得到該對象的鎖;

2. synchronized-代碼塊 說明

  • * 使用synchronized聲明的方法在某些情況下是比較極端的(存在弊端):線程A調用同步的方法執行一段很長時間的任務,那麽B線程就必須等待比較長的時間才能執行;

解決方法:使用synchronized代碼塊去優化代碼執行的時間,也就是減少鎖的粒度;

  • * 特別註意一個問題:就是不要使用String的常量進行加鎖,會出現死循環的問題。
  • * 鎖對象的改變問題:當使用一個對象進行加鎖的時候,要註意對象本身發生變化的時候,那麽持有的鎖就不同。
  • 如果對象本身不發生變化,那麽依然是同步的,即使對象的屬性發生變化也是同步的。

實例:
SyncDubbo1.java 和 SyncDubbo2.java

/**
 * synchronized的重入
 * @@author Maozw
 *
 */
public class SyncDubbo1 {

  public synchronized void method1(){
    System.out.println(
"method1.."); method2(); } public synchronized void method2(){ System.out.println("method2.."); method3(); } public synchronized void method3(){ System.out.println("method3.."); } public static void main(String[] args) { final SyncDubbo1 sd = new SyncDubbo1(); Thread t1
= new Thread(new Runnable() { @Override public void run() { sd.method1(); } }); t1.start(); } }
/**
 * synchronized的重入
 * @@author Maozw
 *
 */
public class SyncDubbo2 {

  static class Main {
    public int i = 10;
    public synchronized void operationSup(){
      try {
        i--;
        System.out.println("Main print i = " + i);
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  static class Sub extends Main {
    public synchronized void operationSub(){
      try {
        while(i > 0) {
          i--;
          System.out.println("Sub print i = " + i);
          Thread.sleep(100);        
          this.operationSup();
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) {

    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        Sub sub = new Sub();
        sub.operationSub();
      }
    });

    t1.start();
  }
}

出現異常會釋放鎖

示例:SyncException.java
說明:對於web程序,異常釋放鎖的情況,如果來不及及時處理,很可能對應用程序的業務邏輯產出錯誤:如執行一個隊列任務,很多對象都去等待第一個對象執行完成並釋放鎖,但是第一個對象由於異常原因,導致業務邏輯沒有正常執行完成,就釋放了鎖,那麽後續任務就會產生一些問題;所以這個問題需要註意;

/**
 * synchronized異常
 * @@author Maozw
 *
 */
public class SyncException {

  private int i = 0;
  public synchronized void operation(){
    while(true){
      try {
        i++;
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + " , i = " + i);
        if(i == 20){
          //Integer.parseInt("a");
          throw new RuntimeException();
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) {

    final SyncException se = new SyncException();
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        se.operation();
      }
    },"t1");
    t1.start();
  }
}

synchronized-代碼塊 說明

使用synchronized聲明的方法在某些情況下是比較極端的(存在弊端):線程A調用同步的方法執行一段很長時間的任務,那麽B線程就必須等待比較長的時間才能執行;
* 解決方法:使用synchronized代碼塊去優化代碼執行的時間,也就是減少鎖的粒度;
* 示例:ObjectLock.java
* 說明:synchronized可以使用任務的Object對象進行加鎖,用法比較靈活;

技術分享圖片
 1 /**
 2  * 使用synchronized代碼塊加鎖,比較靈活
 3  * @@author Maozw
 4  *
 5  */
 6 public class ObjectLock {
 7 
 8   public void method1(){
 9     synchronized (this) {    //對象鎖
10       try {
11         System.out.println("do method1..");
12         Thread.sleep(2000);
13       } catch (InterruptedException e) {
14         e.printStackTrace();
15       }
16     }
17   }
18 
19   public void method2(){        //類鎖
20     synchronized (ObjectLock.class) {
21       try {
22         System.out.println("do method2..");
23         Thread.sleep(2000);
24       } catch (InterruptedException e) {
25         e.printStackTrace();
26       }
27     }
28   }
29 
30   private Object lock = new Object();
31   public void method3(){        //任何對象鎖
32     synchronized (lock) {
33       try {
34         System.out.println("do method3..");
35         Thread.sleep(2000);
36       } catch (InterruptedException e) {
37         e.printStackTrace();
38       }
39     }
40   }
41 
42 
43   public static void main(String[] args) {
44 
45     final ObjectLock objLock = new ObjectLock();
46     Thread t1 = new Thread(new Runnable() {
47       @Override
48       public void run() {
49         objLock.method1();
50       }
51     });
52     Thread t2 = new Thread(new Runnable() {
53       @Override
54       public void run() {
55         objLock.method2();
56       }
57     });
58     Thread t3 = new Thread(new Runnable() {
59       @Override
60       public void run() {
61         objLock.method3();
62       }
63     });
64 
65     t1.start();
66     t2.start();
67     t3.start();
68   }
69 }
View Code

特別註意一個問題:就是不要使用String的常量進行加鎖,會出現死循環的問題。

* 示例:StringLock.java
* 說明:

/**
 * synchronized代碼塊對字符串的鎖,註意String常量池的緩存功能
 * @@author Maozw
 *
 */
public class StringLock {

  public void method() {
    //new String("字符串常量")
    synchronized ("字符串常量") {
      try {
        while(true){
          System.out.println("當前線程 : "  + Thread.currentThread().getName() + "開始");
          Thread.sleep(1000);        
          System.out.println("當前線程 : "  + Thread.currentThread().getName() + "結束");
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) {
    final StringLock stringLock = new StringLock();
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        stringLock.method();
      }
    },"t1");
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        stringLock.method();
      }
    },"t2");

    t1.start();
    t2.start();
  }
}

鎖對象的改變問題:

* 說明:

  • 1. 當使用一個對象進行加鎖的時候,要註意對象本身發生變化的時候,那麽持有的鎖就不同。
  • 2. 如果對象本身不發生變化,那麽依然是同步的,即使對象的屬性發生變化也是同步的。

* 示例:ModifyLock.java

技術分享圖片
  /**
   * 同一對象屬性的修改不會影響鎖的情況
   * @@author Maozw
   *
   */
  public class ModifyLock {

    private String name ;
    private int age ;

    public String getName() {
      return name;
    }
    public void setName(String name) {
      this.name = name;
    }
    public int getAge() {
      return age;
    }
    public void setAge(int age) {
      this.age = age;
    }

    public synchronized void changeAttributte(String name, int age) {
      try {
        System.out.println("當前線程 : "  + Thread.currentThread().getName() + " 開始");
        this.setName(name);
        this.setAge(age);

        System.out.println("當前線程 : "  + Thread.currentThread().getName() + " 修改對象內容為: "
            + this.getName() + ", " + this.getAge());

        Thread.sleep(2000);
        System.out.println("當前線程 : "  + Thread.currentThread().getName() + " 結束");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }

    public static void main(String[] args) {
      final ModifyLock modifyLock = new ModifyLock();
      Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
          modifyLock.changeAttributte("張三", 20);
        }
      },"t1");
      Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
          modifyLock.changeAttributte("李四", 21);
        }
      },"t2");

      t1.start();
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      t2.start();
    }
  }
View Code

死鎖問題 :

* 說明: 在設計程序時就應該避免雙方相互持有對方的鎖的情況
* 示例:

技術分享圖片
/**
   * 死鎖問題,在設計程序時就應該避免雙方相互持有對方的鎖的情況
   * @@author Maozw
   *
   */
  public class DeadLock implements Runnable{

    private String tag;
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public void setTag(String tag){
      this.tag = tag;
    }

    @Override
    public void run() {
      if(tag.equals("a")){
        synchronized (lock1) {
          try {
            System.out.println("當前線程 : "  + Thread.currentThread().getName() + " 進入lock1執行");
            Thread.sleep(2000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          synchronized (lock2) {
            System.out.println("當前線程 : "  + Thread.currentThread().getName() + " 進入lock2執行");
          }
        }
      }
      if(tag.equals("b")){
        synchronized (lock2) {
          try {
            System.out.println("當前線程 : "  + Thread.currentThread().getName() + " 進入lock2執行");
            Thread.sleep(2000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          synchronized (lock1) {
            System.out.println("當前線程 : "  + Thread.currentThread().getName() + " 進入lock1執行");
          }
        }
      }
    }

    public static void main(String[] args) {

      DeadLock d1 = new DeadLock();
      d1.setTag("a");
      DeadLock d2 = new DeadLock();
      d2.setTag("b");

      Thread t1 = new Thread(d1, "t1");
      Thread t2 = new Thread(d2, "t2");

      t1.start();
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      t2.start();
    }
  }
View Code

5.並發編程-synchronized 細節說明