5.並發編程-synchronized 細節說明
阿新 • • 發佈:2018-11-20
dubbo read zed ext tro 當前 使用 current runnable 1. synchronized-鎖重入 & 異常釋放鎖 說明
2. synchronized-代碼塊 說明
synchronized-代碼塊 說明
並發編程-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 細節說明