前言:這是在慕課網上學習劍指Java面試-Offer直通車時所做的筆記,主要供本人複習之用.
目錄
第一章 線程安全問題誘因
1.存在共享數據(也稱臨界資源)
2.存在多條線程共同操作這些共享數據
解決問題的根本方法:
第二章 互斥鎖
第一章的問題可以用互斥鎖解決,而關鍵字sychronized滿足了上述的要求,實現了互斥鎖的功能,在線程同步中扮演了非常重要的作用.可以保證在一個時刻只有一個線程可以執行某個方法或者某個代碼塊,同時synchronized也可以保證一個線程的變化.主要是共享數據的變化,保證這個變化被其它線程鎖看到.
synchronized鎖的不是代碼,鎖的都是對象.堆是線程間共享的,是和程序員打交道最多的區域,因此恰當合理的給一個對象上鎖,是解決線程安全問題的關鍵.
根據獲取的鎖的分類:獲取對象鎖和獲取類鎖
第三章 獲取對象鎖
獲取對象鎖的兩種方法:
1.第一種用法是我們用到的同步代碼塊,主要通過調用synchronized(this),synchronized(類實例對象),在獲取了這些對象的鎖之後去執行花括號{}裏的內容,鎖就是()中的實例對象.
2.第二種是同步非靜態方法,即以synchronized修飾符作爲前綴的method,鎖是當前對象的實例對象.
3.1 實例代碼1
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
syncObjectBlock1();
} else if (threadName.startsWith("C")) {
syncObjectMethod1();
}
}
/**
* 異步方法
*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 方法中有 synchronized(this|object) {} 同步代碼塊
*/
private void syncObjectBlock1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* synchronized 修飾非靜態方法
*/
private synchronized void syncObjectMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我們對異步,同步塊,同步方法都創建兩個線程.然後傳入同一個對象鎖.注意這裏只new 了一個syncThread.
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread A_thread1 = new Thread(syncThread, "A_thread1");
Thread A_thread2 = new Thread(syncThread, "A_thread2");
Thread B_thread1 = new Thread(syncThread, "B_thread1");
Thread B_thread2 = new Thread(syncThread, "B_thread2");
Thread C_thread1 = new Thread(syncThread, "C_thread1");
Thread C_thread2 = new Thread(syncThread, "C_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
}
}
輸出結果:
B_thread1_SyncObjectBlock1: 19:11:12
A_thread1_Async_Start: 19:11:12
C_thread1_SyncObjectMethod1: 19:11:12
B_thread2_SyncObjectBlock1: 19:11:12
C_thread1_SyncObjectMethod1_Start: 19:11:12
A_thread2_Async_Start: 19:11:12
A_thread1_Async_End: 19:11:13
A_thread2_Async_End: 19:11:13
C_thread1_SyncObjectMethod1_End: 19:11:13
B_thread2_SyncObjectBlock1_Start: 19:11:13
B_thread2_SyncObjectBlock1_End: 19:11:14
B_thread1_SyncObjectBlock1_Start: 19:11:14
B_thread1_SyncObjectBlock1_End: 19:11:15
C_thread2_SyncObjectMethod1: 19:11:15
C_thread2_SyncObjectMethod1_Start: 19:11:15
C_thread2_SyncObjectMethod1_End: 19:11:16
分析:先對相同類別的方法的行爲進行分析.
相同類別對比:
對於A:兩個線程是異步執行的,因爲thread1start了之後並未等待thread2並未等待thread1結束纔去執行,而是thread1 start之後,thread2採取start.同時也可以看到A類的thread並未受到其它的使用同步鎖的線程的影響,之所以不受同步的影響,是因爲A類線程的訪問方法並沒有同步代碼塊即沒有獲取同步鎖的需求,也就不會受其它代碼塊的影響.
對於B:我們發現B類線程是同步的,一個線程在訪問對象的代碼塊時,另外一個訪問對象的同步代碼塊的線程會被阻塞,我們發現是要等一個線程(B2)end後另一個線程(B1)纔會start.原因是因爲線程需要等待當前對象的SyncThread的同步鎖,但是synchronized之外的代碼塊依然是異步的.
對於C:C類的線程訪問的是synchronized的非靜態的修飾方法,可以看到C類是全部同步的,C1先start然後end之後再到thread2去start和end.
跨類別對比:
對比B與C:先把synchronized外面的代碼排除掉,即只看B與C的start與end,我們看到是C1先start的,除了C1先運行完了,其它的B1,B2,C2是不會先運行的.這也就證明了同步方法(在方法前加synchronized)與同步塊(synchronized與this)它們鎖的是同一個對象也就是公用的是this對象.
3.2 實例代碼2
將3.1的主方法的代碼換成如下,SyncThread方法不變.
public class SyncDemo {
public static void main(String[] args) {
Thread A_thread1 = new Thread(new SyncThread(), "A_thread1");
Thread A_thread2 = new Thread(new SyncThread(), "A_thread2");
Thread B_thread1 = new Thread(new SyncThread(), "B_thread1");
Thread B_thread2 = new Thread(new SyncThread(), "B_thread2");
Thread C_thread1 = new Thread(new SyncThread(), "C_thread1");
Thread C_thread2 = new Thread(new SyncThread(), "C_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
}
}
運行後發現變成了全異步的,也就是說線程訪問不同對象的synchronized代碼塊或者synchronized非靜態方法都是異步的,同一個類的不同對象的對象鎖是互不干擾的.
A_thread2_Async_Start: 19:46:48
C_thread1_SyncObjectMethod1: 19:46:48
A_thread1_Async_Start: 19:46:48
B_thread1_SyncObjectBlock1: 19:46:48
B_thread2_SyncObjectBlock1: 19:46:48
C_thread2_SyncObjectMethod1: 19:46:48
C_thread1_SyncObjectMethod1_Start: 19:46:48
B_thread1_SyncObjectBlock1_Start: 19:46:48
B_thread2_SyncObjectBlock1_Start: 19:46:48
C_thread2_SyncObjectMethod1_Start: 19:46:48
B_thread1_SyncObjectBlock1_End: 19:46:49
A_thread1_Async_End: 19:46:49
B_thread2_SyncObjectBlock1_End: 19:46:49
C_thread1_SyncObjectMethod1_End: 19:46:49
C_thread2_SyncObjectMethod1_End: 19:46:49
A_thread2_Async_End: 19:46:49
第四章 類鎖
它們都是和類相關的,因此我們用的是類鎖,類鎖實際上是通過對象鎖實現的,即類的class的對象鎖,每個類只有一個Class對象每個類只有一個類鎖.
4.1 實例代碼1
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
syncObjectBlock1();
} else if (threadName.startsWith("C")) {
syncObjectMethod1();
} else if (threadName.startsWith("D")) {
syncClassBlock1();
} else if (threadName.startsWith("E")) {
syncClassMethod1();
}
}
/**
* 異步方法
*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 方法中有 synchronized(this|object) {} 同步代碼塊
*/
private void syncObjectBlock1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* synchronized 修飾非靜態方法
*/
private synchronized void syncObjectMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void syncClassBlock1() {
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (SyncThread.class) {
try {
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized static void syncClassMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread A_thread1 = new Thread(syncThread, "A_thread1");
Thread A_thread2 = new Thread(syncThread, "A_thread2");
Thread D_thread1 = new Thread(syncThread, "D_thread1");
Thread D_thread2 = new Thread(syncThread, "D_thread2");
Thread E_thread1 = new Thread(syncThread, "E_thread1");
Thread E_thread2 = new Thread(syncThread, "E_thread2");
A_thread1.start();
A_thread2.start();
D_thread1.start();
D_thread2.start();
E_thread1.start();
E_thread2.start();
}
}
運行結果:結論與上面是一致的.這裏是按順序依次執行完E1,D2,D1,E2.執行的是synchronized中的內容.
D_thread1_SyncClassBlock1: 19:54:07
A_thread2_Async_Start: 19:54:07
E_thread1_SyncClassMethod1: 19:54:07
A_thread1_Async_Start: 19:54:07
D_thread2_SyncClassBlock1: 19:54:07
E_thread1_SyncClassMethod1_Start: 19:54:07
A_thread2_Async_End: 19:54:08
A_thread1_Async_End: 19:54:08
E_thread1_SyncClassMethod1_End: 19:54:08
D_thread2_SyncClassBlock1_Start: 19:54:08
D_thread2_SyncClassBlock1_End: 19:54:09
D_thread1_SyncClassBlock1_Start: 19:54:09
D_thread1_SyncClassBlock1_End: 19:54:10
E_thread2_SyncClassMethod1: 19:54:10
E_thread2_SyncClassMethod1_Start: 19:54:10
E_thread2_SyncClassMethod1_End: 19:54:11
4.2 實例代碼2
修改4.1主方法中的代碼如如下:
public class SyncDemo {
public static void main(String[] args) {
Thread A_thread1 = new Thread(new SyncThread(), "A_thread1");
Thread A_thread2 = new Thread(new SyncThread(), "A_thread2");
Thread D_thread1 = new Thread(new SyncThread(), "D_thread1");
Thread D_thread2 = new Thread(new SyncThread(), "D_thread2");
Thread E_thread1 = new Thread(new SyncThread(), "E_thread1");
Thread E_thread2 = new Thread(new SyncThread(), "E_thread2");
A_thread1.start();
A_thread2.start();
D_thread1.start();
D_thread2.start();
E_thread1.start();
E_thread2.start();
}
}
輸出如下,可以看到執行爲E1,D2,D1,E2,這與3.2的完全異步不同,因爲這裏用的是類鎖.
A_thread2_Async_Start: 19:59:37
E_thread1_SyncClassMethod1: 19:59:37
D_thread1_SyncClassBlock1: 19:59:37
A_thread1_Async_Start: 19:59:37
D_thread2_SyncClassBlock1: 19:59:37
E_thread1_SyncClassMethod1_Start: 19:59:37
A_thread1_Async_End: 19:59:38
E_thread1_SyncClassMethod1_End: 19:59:38
A_thread2_Async_End: 19:59:38
D_thread2_SyncClassBlock1_Start: 19:59:38
D_thread2_SyncClassBlock1_End: 19:59:39
D_thread1_SyncClassBlock1_Start: 19:59:39
D_thread1_SyncClassBlock1_End: 19:59:40
E_thread2_SyncClassMethod1: 19:59:40
E_thread2_SyncClassMethod1_Start: 19:59:40
E_thread2_SyncClassMethod1_End: 19:59:41
4.3 實例代碼3
對象鎖與類鎖之間是互不干擾的,無論傳入的是同一個對象還是不同的對象.
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread A_thread1 = new Thread(syncThread, "A_thread1");
Thread A_thread2 = new Thread(syncThread, "A_thread2");
Thread B_thread1 = new Thread(syncThread, "B_thread1");
Thread B_thread2 = new Thread(syncThread, "B_thread2");
Thread C_thread1 = new Thread(syncThread, "C_thread1");
Thread C_thread2 = new Thread(syncThread, "C_thread2");
Thread D_thread1 = new Thread(syncThread, "D_thread1");
Thread D_thread2 = new Thread(syncThread, "D_thread2");
Thread E_thread1 = new Thread(syncThread, "E_thread1");
Thread E_thread2 = new Thread(syncThread, "E_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
D_thread1.start();
D_thread2.start();
E_thread1.start();
E_thread2.start();
}
}
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
syncObjectBlock1();
} else if (threadName.startsWith("C")) {
syncObjectMethod1();
} else if (threadName.startsWith("D")) {
syncClassBlock1();
} else if (threadName.startsWith("E")) {
syncClassMethod1();
}
}
/**
* 異步方法
*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 方法中有 synchronized(this|object) {} 同步代碼塊
*/
private void syncObjectBlock1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* synchronized 修飾非靜態方法
*/
private synchronized void syncObjectMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void syncClassBlock1() {
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (SyncThread.class) {
try {
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized static void syncClassMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出如下,可以看到C2的start與E2的start是一起執行的,所以說互不干擾.
E_thread1_SyncClassMethod1: 20:06:19
D_thread2_SyncClassBlock1: 20:06:19
D_thread1_SyncClassBlock1: 20:06:19
A_thread1_Async_Start: 20:06:19
B_thread1_SyncObjectBlock1: 20:06:19
C_thread1_SyncObjectMethod1: 20:06:19
A_thread2_Async_Start: 20:06:19
B_thread2_SyncObjectBlock1: 20:06:19
E_thread1_SyncClassMethod1_Start: 20:06:19
C_thread1_SyncObjectMethod1_Start: 20:06:19
C_thread1_SyncObjectMethod1_End: 20:06:20
E_thread1_SyncClassMethod1_End: 20:06:20
A_thread1_Async_End: 20:06:20
A_thread2_Async_End: 20:06:20
B_thread2_SyncObjectBlock1_Start: 20:06:20
D_thread1_SyncClassBlock1_Start: 20:06:20
D_thread1_SyncClassBlock1_End: 20:06:21
B_thread2_SyncObjectBlock1_End: 20:06:21
D_thread2_SyncClassBlock1_Start: 20:06:21
B_thread1_SyncObjectBlock1_Start: 20:06:21
B_thread1_SyncObjectBlock1_End: 20:06:22
D_thread2_SyncClassBlock1_End: 20:06:22
C_thread2_SyncObjectMethod1: 20:06:22
E_thread2_SyncClassMethod1: 20:06:22
C_thread2_SyncObjectMethod1_Start: 20:06:22
E_thread2_SyncClassMethod1_Start: 20:06:22
C_thread2_SyncObjectMethod1_End: 20:06:23
E_thread2_SyncClassMethod1_End: 20:06:23
第五章 總結