一城豁、概念
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)方法诚卸,非靜態(tài)方法
四、synchronized 的用法詳解
這里根據(jù)獲取的鎖分類來分析 synchronized 的用法
1绘迁、對(duì)象鎖
這個(gè)對(duì)象是新建的合溺,跟其他對(duì)象沒有任何關(guān)系:
/** * synchronized 修飾非靜態(tài)方法 */
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); **synchronized (new SyncThread())** { try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
文章后面所有例子都是采用這四個(gè)線程,只是對(duì)方法進(jìn)行修改:
SyncThread syncThread = new SyncThread();
Thread F_thread1 = new Thread(new SyncThread(), "F_thread1");
Thread F_thread2 = new Thread(new SyncThread(), "F_thread2");
Thread F_thread3 = new Thread(syncThread, "F_thread3");
Thread F_thread4 = new Thread(syncThread, "F_thread4");
F_thread1.start();
F_thread2.start();
F_thread3.start();
F_thread4.start();
運(yùn)行結(jié)果如下:
四個(gè)線程同時(shí)開始缀台,同時(shí)結(jié)束棠赛,因?yàn)樽鳛殒i的對(duì)象與線程是屬于不同的實(shí)例。
2、采用類鎖恭朗,無所謂哪個(gè)類屏镊,都會(huì)被攔截:
/** * synchronized 修飾非靜態(tài)方法 */
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); **synchronized (MainActivity.class****)** { try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運(yùn)行結(jié)果如下:
可以發(fā)現(xiàn),采用類鎖一次只能通過一個(gè)痰腮。即使采用的是 **MainActivity.class **這個(gè)類鎖而芥。
3、采用 this 對(duì)象鎖:
/** * synchronized 修飾非靜態(tài)方法 */
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); **synchronized (this****)** { try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運(yùn)行結(jié)果如下:
可以發(fā)現(xiàn)線程1膀值,2同時(shí)結(jié)束棍丐,3,4有先后沧踏,原因是3歌逢,4同屬于一個(gè)實(shí)例。
4翘狱、synchronized 修飾方法
作用范圍是整個(gè)方法秘案,所以方法中所有的代碼都是同步的:
/** * synchronized 修飾非靜態(tài)方法 */
**private synchronized void** **sync5**() {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
修飾非靜態(tài)方法:
對(duì)于非靜態(tài)方法,同一個(gè)實(shí)例的線程訪問會(huì)被攔截潦匈,非同一實(shí)例可以同時(shí)訪問阱高。 即此時(shí)是默認(rèn)對(duì)象鎖(this)。
修飾靜態(tài)方法結(jié)果
可以看出來茬缩,靜態(tài)方法默認(rèn)類鎖赤惊。
總結(jié)
1、對(duì)于靜態(tài)方法凰锡,由于此時(shí)對(duì)象還未生成未舟,所以只能采用類鎖;
2掂为、只要采用類鎖裕膀,就會(huì)攔截所有線程,只能讓一個(gè)線程訪問勇哗。
3魂角、對(duì)于對(duì)象鎖(this),如果是同一個(gè)實(shí)例智绸,就會(huì)按順序訪問野揪,但是如果是不同實(shí)例,就可以同時(shí)訪問瞧栗。
4斯稳、如果對(duì)象鎖跟訪問的對(duì)象沒有關(guān)系,那么就會(huì)都同時(shí)訪問迹恐。