Java多執行緒技術篇--執行緒的互斥與同步通訊
阿新 • • 發佈:2019-01-26
一、使用銀行轉賬來解釋執行緒安全問題
同一個銀行賬戶,在同一時間發生了轉賬匯款(解釋:你刷卡消費了1000元的同時,你朋友給你轉賬了2000元)。這個時候是兩個執行緒操作同一個資料,但是執行緒執行是隨機的,沒有先後之分,這個問題就引發了執行緒安全問題。
二、使用synchronized程式碼塊及其原理
Synchronized的語義底層是通過一個monitor的物件來完成,其實wait/notify等方法也依賴於monitor物件,
這就是為什麼只有在同步的塊或者方法中才能呼叫wait/notify等方法,否則會丟擲java.lang.IllegalMonitorStateException的異常的原因。
三、使用synchronized方法
package com.sinwao.thread01; public class ThreadSynchronized { public static void main(String[] args) { new ThreadSynchronized().init(); } private void init() { Outputer outputer = new Outputer(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } outputer.output3("Mcgrady"); } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } outputer.output3("Byrant"); } } }).start(); } static class Outputer{ public void output(String name) { int len = name.length(); synchronized (Outputer.class) { for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } public synchronized void output2(String name) { int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } public synchronized static void output3(String name) { int len = name.length(); for (int i = 0; i < len; i++) { System.out.print(name.charAt(i)); } System.out.println(); } } }
四、分析靜態方法所使用的同步監視器物件是什麼
因為output3是一個靜態方法,靜態方法要同步,必須要用到鎖物件,靜態方法執行的時候
不用建立類的例項物件,但是靜態方法的位元組碼物件已經在記憶體裡面了,所以在output中傳入Output.class,就可以實現同步。
重點:靜態方法只與位元組碼物件關聯
五、面試題解析
子執行緒迴圈10次,接著主執行緒執行10 0次,再接著子執行緒迴圈10次,再接著主執行緒迴圈100,如此往復50次。
package com.sinwao.thread01; public class ThreadCommunication { public static void main(String[] args) { Business business = new Business(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50; i++) { /*synchronized (ThreadCommunication.class) { for (int j = 0; j < 10; j++) { System.out.println("子執行緒第" + j + "次執行,迴圈次數 " + i); } }*/ /** * 將這部分程式碼封裝起來,可以很高的解決了synchronized鎖的問題 * 講要用到共同資料(包括同步鎖)或者共同演算法的若干個方法應該歸在同一個類身上 * 這種設計正好體現了高類聚和程式的健壯性 */ business.sub(i); } } }).start(); /** * 加了synchronized之後,子執行緒跟主執行緒互斥 */ for (int i = 0; i < 50; i++) { /*synchronized (ThreadCommunication.class) { for (int j = 0; j < 10; j++) { System.out.println("主執行緒第" + j + "次執行,迴圈次數 " + i); } }*/ business.main(i); } } }
package com.sinwao.thread01;
public class Business {
private boolean isSubThread = true;
public synchronized void sub(int i) {
// if (!isSubThread) {
while(!isSubThread) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 0; j < 10; j++) {
System.out.println("子執行緒第" + j + "次執行,迴圈次數 " + i);
}
isSubThread = !isSubThread;
this.notify();
}
public synchronized void main(int i) {
// if (isSubThread) {
while(isSubThread) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 0; j < 100; j++) {
System.out.println("主執行緒第" + j + "次執行,迴圈次數 " + i);
}
isSubThread = !isSubThread;
this.notify();
}
/**
* 使用if和while的區別
* 檢視API的時候發現wait在使用if的時候會出現假喚醒情況,所以官方推薦使用while
*/
}