一生蚁、概念
synchronized 是 Java 中的關(guān)鍵字,是利用鎖的機(jī)制來實(shí)現(xiàn)同步的戏自。
鎖機(jī)制有如下兩種特性:
互斥性:即在同一時(shí)間只允許一個(gè)線程持有某個(gè)對(duì)象鎖邦投,通過這種特性來實(shí)現(xiàn)多線程中的協(xié)調(diào)機(jī)制,這樣在同一時(shí)間只有一個(gè)線程對(duì)需同步的代碼塊(復(fù)合操作)進(jìn)行訪問擅笔≈疽拢互斥性我們也往往稱為操作的原子性屯援。
可見性:必須確保在鎖被釋放之前,對(duì)共享變量所做的修改念脯,對(duì)于隨后獲得該鎖的另一個(gè)線程是可見的(即在獲得鎖時(shí)應(yīng)獲得最新共享變量的值)狞洋,否則另一個(gè)線程可能是在本地緩存的某個(gè)副本上繼續(xù)操作從而引起不一致。
二和二、對(duì)象鎖和類鎖
1. 對(duì)象鎖
在 Java 中徘铝,每個(gè)對(duì)象都會(huì)有一個(gè) monitor 對(duì)象耳胎,這個(gè)對(duì)象其實(shí)就是 Java 對(duì)象的鎖惯吕,通常會(huì)被稱為“內(nèi)置鎖”或“對(duì)象鎖”。類的對(duì)象可以有多個(gè)怕午,所以每個(gè)對(duì)象有其獨(dú)立的對(duì)象鎖废登,互不干擾。
2. 類鎖
在 Java 中郁惜,針對(duì)每個(gè)類也有一個(gè)鎖堡距,可以稱為“類鎖”,類鎖實(shí)際上是通過對(duì)象鎖實(shí)現(xiàn)的兆蕉,即類的 Class 對(duì)象鎖羽戒。每個(gè)類只有一個(gè) Class 對(duì)象,所以每個(gè)類只有一個(gè)類鎖虎韵。
三易稠、synchronized 的用法分類
synchronized 的用法可以從兩個(gè)維度上面分類:
1. 根據(jù)修飾對(duì)象分類
synchronized 可以修飾方法和代碼塊
-
修飾代碼塊
synchronized(this|object) {}
synchronized(類.class) {}
-
修飾方法
修飾非靜態(tài)方法
修飾靜態(tài)方法
2. 根據(jù)獲取的鎖分類
-
獲取對(duì)象鎖
synchronized(this|object) {}
修飾非靜態(tài)方法
-
獲取類鎖
synchronized(類.class) {}
修飾靜態(tài)方法
四、synchronized 的用法詳解
這里根據(jù)獲取的鎖分類來分析 synchronized 的用法
1. 獲取對(duì)象鎖
1.1 對(duì)于同一對(duì)象
- 同步線程類:
class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
sync1();
} else if (threadName.startsWith("C")) {
sync2();
}
}
/**
* 異步方法
*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
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 sync1() {
System.out.println(Thread.currentThread().getName() + "_Sync1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + "_Sync1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* synchronized 修飾非靜態(tài)方法
*/
private synchronized void sync2() {
System.out.println(Thread.currentThread().getName() + "_Sync2: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_Sync2_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync2_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 測試代碼:
public class SyncTest {
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();
}
}
- 運(yùn)行結(jié)果:
B_thread2_Sync1: 14:44:20
A_thread1_Async_Start: 14:44:20
B_thread1_Sync1: 14:44:20
C_thread1_Sync2: 14:44:20
A_thread2_Async_Start: 14:44:20
C_thread1_Sync2_Start: 14:44:20
A_thread1_Async_End: 14:44:22
A_thread2_Async_End: 14:44:22
C_thread1_Sync2_End: 14:44:22
B_thread1_Sync1_Start: 14:44:22
B_thread1_Sync1_End: 14:44:24
B_thread2_Sync1_Start: 14:44:24
B_thread2_Sync1_End: 14:44:26
C_thread2_Sync2: 14:44:26
C_thread2_Sync2_Start: 14:44:26
C_thread2_Sync2_End: 14:44:28
-
結(jié)果分析:
- A 類線程訪問方法中沒有同步代碼塊包蓝,A 類線程是異步的驶社,所以有線程訪問對(duì)象的同步代碼塊時(shí),另外的線程可以訪問該對(duì)象的非同步代碼塊:
A_thread1_Async_Start: 14:44:20 A_thread2_Async_Start: 14:44:20 A_thread1_Async_End: 14:44:22 A_thread2_Async_End: 14:44:22
- B 類線程訪問的方法中有同步代碼塊测萎,B 類線程是同步的亡电,一個(gè)線程在訪問對(duì)象的同步代碼塊,另一個(gè)訪問對(duì)象的同步代碼塊的線程會(huì)被阻塞:
B_thread1_Sync1_Start: 14:44:22 B_thread1_Sync1_End: 14:44:24 B_thread2_Sync1_Start: 14:44:24 B_thread2_Sync1_End: 14:44:26
- synchronized(this|object) {} 代碼塊 {} 之外的代碼依然是異步的:
B_thread2_Sync1: 14:44:20 B_thread1_Sync1: 14:44:20
- C 類線程訪問的是 synchronized 修飾非靜態(tài)方法硅瞧,C 類線程是同步的份乒,一個(gè)線程在訪問對(duì)象的同步代方法,另一個(gè)訪問對(duì)象同步方法的線程會(huì)被阻塞:
C_thread1_Sync2_Start: 14:44:20 C_thread1_Sync2_End: 14:44:22 C_thread2_Sync2_Start: 14:44:26 C_thread2_Sync2_End: 14:44:28
- synchronized 修飾非靜態(tài)方法腕唧,作用范圍是整個(gè)方法或辖,所以方法中所有的代碼都是同步的:
C_thread1_Sync2: 14:44:20 C_thread2_Sync2: 14:44:26
- 由結(jié)果可知 B 類和 C 類線程順序執(zhí)行,<strong>類中 synchronized(this|object) {} 代碼塊和 synchronized 修飾非靜態(tài)方法獲取的鎖是同一個(gè)鎖四苇,即該類的對(duì)象的對(duì)象鎖孝凌。</strong>所以 B 類線程和 C 類線程也是同步的:
B_thread1_Sync1_Start: 14:44:22 B_thread1_Sync1_End: 14:44:24 C_thread1_Sync2_Start: 14:44:20 C_thread1_Sync2_End: 14:44:22 B_thread2_Sync1_Start: 14:44:24 B_thread2_Sync1_End: 14:44:26 C_thread2_Sync2_Start: 14:44:26 C_thread2_Sync2_End: 14:44:28
1.2 對(duì)于不同對(duì)象
- 修改測試代碼為:
public class SyncTest {
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();
}
}
- 運(yùn)行結(jié)果:
A_thread2_Async_Start: 15:01:34
C_thread2_Sync2: 15:01:34
B_thread2_Sync1: 15:01:34
C_thread1_Sync2: 15:01:34
B_thread2_Sync1_Start: 15:01:34
B_thread1_Sync1: 15:01:34
C_thread1_Sync2_Start: 15:01:34
A_thread1_Async_Start: 15:01:34
C_thread2_Sync2_Start: 15:01:34
B_thread1_Sync1_Start: 15:01:34
C_thread1_Sync2_End: 15:01:36
A_thread1_Async_End: 15:01:36
C_thread2_Sync2_End: 15:01:36
B_thread2_Sync1_End: 15:01:36
B_thread1_Sync1_End: 15:01:36
A_thread2_Async_End: 15:01:36
-
結(jié)果分析:
- 兩個(gè)線程訪問不同對(duì)象的 synchronized(this|object) {} 代碼塊和 synchronized 修飾非靜態(tài)方法是異步的,同一個(gè)類的不同對(duì)象的對(duì)象鎖互不干擾月腋。
2 獲取類鎖
2.1 對(duì)于同一對(duì)象
- 同步線程類:
class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
sync1();
} else if (threadName.startsWith("C")) {
sync2();
}
}
/**
* 異步方法
*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 方法中有 synchronized(類.class) {} 同步代碼塊
*/
private void sync1() {
System.out.println(Thread.currentThread().getName() + "_Sync1: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (SyncThread.class) {
try {
System.out.println(Thread.currentThread().getName() + "_Sync1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* synchronized 修飾靜態(tài)方法
*/
private synchronized static void sync2() {
System.out.println(Thread.currentThread().getName() + "_Sync2: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_Sync2_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync2_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 測試代碼:
public class SyncTest {
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();
}
}
- 運(yùn)行結(jié)果:
B_thread1_Sync1: 15:08:13
C_thread1_Sync2: 15:08:13
B_thread2_Sync1: 15:08:13
A_thread1_Async_Start: 15:08:13
C_thread1_Sync2_Start: 15:08:13
A_thread2_Async_Start: 15:08:13
C_thread1_Sync2_End: 15:08:15
A_thread2_Async_End: 15:08:15
A_thread1_Async_End: 15:08:15
B_thread2_Sync1_Start: 15:08:15
B_thread2_Sync1_End: 15:08:17
B_thread1_Sync1_Start: 15:08:17
B_thread1_Sync1_End: 15:08:19
C_thread2_Sync2: 15:08:19
C_thread2_Sync2_Start: 15:08:19
C_thread2_Sync2_End: 15:08:21
-
結(jié)果分析:
- 由結(jié)果可以看出蟀架,在同一對(duì)象的情況下瓣赂,synchronized(類.class) {} 代碼塊或 synchronized 修飾靜態(tài)方法和 synchronized(this|object) {} 代碼塊和 synchronized 修飾非靜態(tài)方法的行為一致。
2.2 對(duì)于不同對(duì)象
- 修改測試代碼為:
public class SyncTest {
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();
}
}
- 運(yùn)行結(jié)果:
A_thread2_Async_Start: 15:17:28
B_thread2_Sync1: 15:17:28
A_thread1_Async_Start: 15:17:28
B_thread1_Sync1: 15:17:28
C_thread1_Sync2: 15:17:28
C_thread1_Sync2_Start: 15:17:28
C_thread1_Sync2_End: 15:17:30
A_thread2_Async_End: 15:17:30
B_thread1_Sync1_Start: 15:17:30
A_thread1_Async_End: 15:17:30
B_thread1_Sync1_End: 15:17:32
B_thread2_Sync1_Start: 15:17:32
B_thread2_Sync1_End: 15:17:34
C_thread2_Sync2: 15:17:34
C_thread2_Sync2_Start: 15:17:34
C_thread2_Sync2_End: 15:17:36
-
結(jié)果分析:
- <strong>兩個(gè)線程訪問不同對(duì)象的 synchronized(類.class) {} 代碼塊或 synchronized 修飾靜態(tài)方法還是同步的片拍,類中 synchronized(類.class) {} 代碼塊和 synchronized 修飾靜態(tài)方法獲取的鎖是類鎖煌集。對(duì)于同一個(gè)類的不同對(duì)象的類鎖是同一個(gè)。</strong>
3 類中同時(shí)有 synchronized(類.class) {} 代碼塊或 synchronized 修飾靜態(tài)方法和 synchronized(this|object) {} 代碼塊和 synchronized 修飾非靜態(tài)方法時(shí)會(huì)怎樣捌省?
- 修改同步線程類:
class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
sync1();
} else if (threadName.startsWith("C")) {
sync2();
}
}
/**
* 異步方法
*/
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "_Async_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Async_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* synchronized 修飾非靜態(tài)方法
*/
private synchronized void sync1() {
try {
System.out.println(Thread.currentThread().getName() + "_Sync1_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync1_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* synchronized 修飾靜態(tài)方法
*/
private synchronized static void sync2() {
try {
System.out.println(Thread.currentThread().getName() + "_Sync2_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "_Sync2_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 修改測試代碼:
public class SyncTest {
public static void main(String... args) {
Thread B_thread1 = new Thread(syncThread, "B_thread1");
Thread C_thread1 = new Thread(syncThread, "C_thread1");
B_thread1.start();
C_thread1.start();
}
}
- 運(yùn)行結(jié)果:
B_thread1_Sync1_Start: 15:35:21
C_thread1_Sync2_Start: 15:35:21
B_thread1_Sync1_End: 15:35:23
C_thread1_Sync2_End: 15:35:23
-
運(yùn)行結(jié)果分析:
- 由結(jié)果可以看到 B 類線程和 C 類線程是異步的苫纤,即 synchronized 修飾靜態(tài)方法和 synchronized 修飾非靜態(tài)方法是異步的,對(duì)于 synchronized(類.class) {} 代碼塊和 synchronized(this|object) {} 代碼塊也是一樣的纲缓。<strong>所以對(duì)象鎖和類鎖是獨(dú)立的卷拘,互不干擾。</strong>
4 補(bǔ)充
-
synchronized關(guān)鍵字不能繼承祝高。
對(duì)于父類中的 synchronized 修飾方法栗弟,子類在覆蓋該方法時(shí),默認(rèn)情況下不是同步的工闺,必須顯示的使用 synchronized 關(guān)鍵字修飾才行乍赫。
在定義接口方法時(shí)不能使用synchronized關(guān)鍵字。
構(gòu)造方法不能使用synchronized關(guān)鍵字陆蟆,但可以使用synchronized代碼塊來進(jìn)行同步雷厂。