Java中的線程以前也提到過,可是仍是想再詳細的學習一下,跟着張孝祥老師,系統的再學習一下。面試
1、線程中的互斥安全
線程安全中的問題解釋:線程安全問題能夠用銀行中的轉帳ide
例題描述:oop
線程A與線程B分別訪問同一個對象的方法,這樣就會存在線程安全的問題,方法的做用是打印出字符串中的每個字符,方法以下:學習
1 public void output(String name) { 2 int len = name.length(); 3 for (int i = 0; i < len; i++) { 4 System.out.print(name.charAt(i)); 5 } 6 System.out.println(); 7 }
線程A和線程B代碼以下:(直接寫在了init()方法中了)測試
1 private void init() { 2 outputer outputer = new outputer(); 3 new Thread(new Runnable() { 4 @Override 5 public void run() { 6 while (true) { 7 try { 8 Thread.sleep(10); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 outputer.output("songshengchao"); 13 } 14 } 15 }).start(); 16 17 new Thread(new Runnable() { 18 @Override 19 public void run() { 20 while (true) { 21 try { 22 Thread.sleep(10); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 outputer.output("songxiaochao"); 27 } 28 } 29 }).start(); 30 }
測試一下,確定會出現線程不安全的問題,這是母庸質疑的事實,測試代碼以下:優化
1 public static void main(String[] args) { 2 new TraditionalThreadSynchronized().init(); 3 }
三種解決辦法,代碼以下:this
1 public class outputer { 2 public void output(String name) { 3 int len = name.length(); 4 synchronized (this) { // 傳進來當前調用方法的對象,要求線程用的是同一個對象 5 //synchronized (outputer.class) { // 這樣和outputer3方法達到線程互斥 6 for (int i = 0; i < len; i++) { 7 System.out.print(name.charAt(i)); 8 } 9 System.out.println(); 10 } 11 } 12 13 // 方法上的鎖對象用的就是this當前對象 14 public synchronized void output2(String name) { 15 int len = name.length(); 16 for (int i = 0; i < len; i++) { 17 System.out.print(name.charAt(i)); 18 } 19 System.out.println(); 20 } 21 22 // output3 想和output方法達到線程互斥 23 public static synchronized void output3(String name) { 24 int len = name.length(); 25 for (int i = 0; i < len; i++) { 26 System.out.print(name.charAt(i)); 27 } 28 System.out.println(); 29 } 30 }
注意:至於第三種靜態的synchronized方法,在和第一個方法共用時,第一種方法必定是用當前對象的class字節碼文件,才能確保兩個方法用的同一個對象。spa
2、線程互斥與通訊的經典面試題線程
面試題:子線程循環10次,接着主線程循環100次,接着又回到子線程循環10次,接着在主線程循環100次,如此循環50次,程序如何寫???
經驗之談,設計思想,設計思路:
要用到共同數據(包括同步鎖)的若干個方法應該歸在同一個類上,這種設計正好提現了程序的高內聚與健壯性
思路:先寫主線程與子線程的循環,而後在考慮輪流執行。先考慮循環,代碼以下:
1 public class TraditionalThreadCommunication { 2 3 public static void main(String[] args) { 4 5 new Thread(new Runnable() { 6 7 @Override 8 public void run() { 9 for (int i = 1; i <= 50; i++) { 10 synchronized (TraditionalThreadCommunication.class) { 11 for (int j = 1; j <= 10; j++) { 12 System.out.println("sub thread sequece of" + j + ", loop of " + i); 13 } 14 } 15 16 } 17 } 18 }).start(); 19 20 // 自己main方法就是主線程,直接能夠寫循環代碼 21 for (int i = 1; i <= 50; i++) { 22 synchronized (TraditionalThreadCommunication.class) { 23 for (int j = 1; j <= 100; j++) { 24 System.out.println("main thread sequece of" + j + ", loop of " + i); 25 } 26 } 27 } 28 29 } 30 }
代碼優化,用面向對象的思想,將那些代碼放到一個公共的類中,而後執行類中的不一樣方法,優化成一個公共的類,代碼以下:
1 public class Business { 2 3 public synchronized void sub(int i){ 4 for (int j = 1; j <= 10; j++) { 5 System.out.println("sub thread sequece of" + j + ", loop of " + i); 6 } 7 } 8 9 public synchronized void main(int i){ 10 for (int j = 1; j <= 100; j++) { 11 System.out.println("main thread sequece of" + j + ", loop of " + i); 12 } 13 } 14 } 15 16 ---------------------------------------------------------------------------------------------- 17 18 public class TraditionalThreadCommunication { 19 20 21 public static void main(String[] args) { 22 Business business = new Business(); 23 new Thread(new Runnable() { 24 25 @Override 26 public void run() { 27 for (int i = 1; i <= 50; i++) { 28 business.sub(i); 29 } 30 } 31 }).start(); 32 33 // 自己main方法就是主線程,直接能夠寫循環代碼 34 for (int i = 1; i <= 50; i++) { 35 business.main(i); 36 } 37 38 } 39 40 }
最終的完整代碼以下(詳細註釋):
1 public class Business { 2 3 // 是不是子線程執行 默認子線程先執行 4 private boolean bShouldSub = true; 5 6 public synchronized void sub(int i) { 7 // 不是子線程應該執行 讓給主線程 子線程執行等待的方法 8 while (!bShouldSub) { 9 try { 10 this.wait(); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 for (int j = 1; j <= 10; j++) { 16 System.out.println("sub thread sequece of" + j + ", loop of " + i); 17 } 18 // 子線程執行完畢後 讓給主線程執行 19 bShouldSub = false; 20 // 喚醒主線程 21 this.notify(); 22 } 23 24 public synchronized void main(int i) { 25 // 是子線程應該執行 讓給子線程執行 主線程執行等待的方法 26 while (bShouldSub) { 27 try { 28 this.wait(); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 for (int j = 1; j <= 100; j++) { 34 System.out.println("main thread sequece of" + j + ", loop of " + i); 35 } 36 // 主線程執行費完畢後 交給子線程執行 37 bShouldSub = true; 38 // 喚醒子線程 39 this.notify(); 40 } 41 } 42 ------------------------------------------------------------------------------------------------ 43 44 public class TraditionalThreadCommunication { 45 46 public static void main(String[] args) { 47 Business business = new Business(); 48 new Thread(new Runnable() { 49 50 @Override 51 public void run() { 52 for (int i = 1; i <= 50; i++) { 53 business.sub(i); 54 } 55 } 56 }).start(); 57 58 // 自己main方法就是主線程,直接能夠寫循環代碼 59 for (int i = 1; i <= 50; i++) { 60 business.main(i); 61 } 62 63 } 64 65 }