通過分析這兩個用法的分析,我們可以理解java中鎖的概念。一個是實例鎖(鎖在某一個實例對象上,如果該類是單例,那么該鎖也具有全局鎖的概念),一個是全局鎖(該鎖針對的是類,無論實例多少個對象,那么線程都共享該鎖)。實例鎖對應的就是synchronized關鍵字,而類鎖(全局鎖)對應的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。下面的文章做了很好的總結:
1.synchronized與static synchronized 的區別
synchronized是對類的當前實例進行加鎖,防止其他線程同時訪問該類的該實例的所有synchronized塊,注意這里是“類的當前實例”, 類的兩個不同實例就沒有這種約束了。那么static synchronized恰好就是要控制類的所有實例的訪問了,static synchronized是限制線程同時訪問jvm中該類的所有實例同時訪問對應的代碼快。實際上,在類中某方法或某代碼塊中有 synchronized,那么在生成一個該類實例后,改類也就有一個監視快,放置線程並發訪問改實例synchronized保護快,而static synchronized則是所有該類的實例公用一個監視快了,也也就是兩個的區別了,也就是synchronized相當於 this.synchronized,而static synchronized相當於Something.synchronized.
一個日本作者-結成浩的《java多線程設計模式》有這樣的一個列子:
pulbic class Something(){
public synchronized void isSyncA(){}
public synchronized void isSyncB(){}
public static synchronized void cSyncA(){}
public static synchronized void cSyncB(){}
}
那么,假如有Something類的兩個實例x與y,那么下列哪幾組方法何以被1個以上線程同時訪問呢
a. x.isSyncA()與x.isSyncB()
b. x.isSyncA()與y.isSyncA()
c. x.cSyncA()與y.cSyncB()
d. x.isSyncA()與Something.cSyncA()
這里,很清楚的可以判斷:
a情況,都是對同一個實例的synchronized域訪問,因此不能被同時訪問
b情況,是針對不同實例的,因此可以同時被訪問
c情況,因為是static synchronized,所以不同實例之間仍然會被限制,相當於Something.isSyncA()與 Something.isSyncB()了,因此不能被同時訪問。
那么,第d情況呢?,書上的 答案是可以被同時訪問的,答案理由是synchronzied的是實例方法與synchronzied的類方法由於鎖定(lock)不同的原因。
個人分析也就是synchronized 與static synchronized 相當於兩幫派,各自管各自,相互之間就無約束了,可以被同時訪問。目前還不是分清楚java內部設計synchronzied是怎么樣實現的。
實例:
public class TestSynchronized
{
public synchronized void test1()
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
public static synchronized void test2()
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
public static void main(String[] args)
{
final TestSynchronized myt2 = new TestSynchronized();
Thread test1 = new Thread( new Runnable() { public void run() { myt2.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { TestSynchronized.test2(); } }, "test2" );
test1.start();
test2.start();
// TestRunnable tr=new TestRunnable();
// Thread test3=new Thread(tr);
// test3.start();
}
}
運行結果:
test1 : 4
test2 : 4
test1 : 3
test2 : 3
test2 : 2
test1 : 2
test2 : 1
test1 : 1
test1 : 0
test2 : 0
上面代碼synchronized同時修飾靜態方法和實例方法,但是運行結果是交替進行的,這證明了類鎖和對象鎖是兩個不一樣的鎖,控制着不同的區域,它們是互不干擾的。同樣,線程獲得對象鎖的同時,也可以獲得該類鎖,即同時獲得兩個鎖,這是允許的。
結論:A: synchronized static是某個類的范圍,synchronized static cSync{}防止多個線程同時訪問這個 類中的synchronized static 方法。它可以對類的所有對象實例起作用。
B: synchronized 是某實例的范圍,synchronized isSync(){}防止多個線程同時訪問這個實例中的synchronized 方法。
其實總結起來很簡單。
一個鎖的是類對象,一個鎖的是實例對象。
若類對象被lock,則類對象的所有同步方法全被lock;
若實例對象被lock,則該實例對象的所有同步方法全被lock。
2.synchronized方法與synchronized代碼快的區別
synchronized methods(){} 與synchronized(this){}之間沒有什么區別,只是synchronized methods(){} 便於閱讀理解,而synchronized(this){}可以更精確的控制沖突限制訪問區域,有時候表現更高效率。
3.synchronized關鍵字是不能繼承的
這個在http://www.learndiary.com/archives/diaries/2910.htm一文中看到的,我想這一點也是很值得注意的,繼承時子類的覆蓋方法必須顯示定義成synchronized。(但是如果使用繼承開發環境的話,會默認加上synchronized關鍵字)
兩種方式效率比較:
1、同步塊,代碼如下:
package com.bjtest.belen;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestSynchronized {
/**
* @param args
*/
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
final SynchonizedClass sc = new SynchonizedClass();
for(int i=0; i<3; i++){
Runnable runnable = new Runnable(){
public void run() {
try{
cdOrder.await();
sc.start();
cdAnswer.countDown();
}catch(Exception e){
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try{
Thread.sleep((long) (Math.random()*10000));
System.out.println("線程" + Thread.currentThread().getName() +
"發布執行命令");
cdOrder.countDown();
long beginTime = System.currentTimeMillis();
System.out.println("線程" + Thread.currentThread().getName() +
"已經發送命令,正在等待結果");
cdAnswer.await();
System.out.println("線程" + Thread.currentThread().getName() +
"已收到所有響應結果,所用時間為:" + (System.currentTimeMillis()-beginTime));
}catch(Exception e){
e.printStackTrace();
}
service.shutdown();
}
}
class SynchonizedClass{
public void start() throws InterruptedException{
Thread.sleep(100);//執行其它邏輯消耗時間
synchronized(this){
System.out.println("我運行使用了 10 ms");
}
}
}
運行結果如下:
線程main發布執行命令
線程main已經發送命令,正在等待結果
我運行使用了 10 ms
我運行使用了 10 ms
我運行使用了 10 ms
線程main已收到所有響應結果,所用時間為:110
同步方法,代碼如下:
package com.bjtest.belen;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestSynchronized {
/**
* @param args
*/
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
final SynchonizedClass sc = new SynchonizedClass();
for(int i=0; i<3; i++){
Runnable runnable = new Runnable(){
public void run() {
try{
cdOrder.await();
sc.start();
cdAnswer.countDown();
}catch(Exception e){
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try{
Thread.sleep((long) (Math.random()*10000));
System.out.println("線程" + Thread.currentThread().getName() +
"發布執行命令");
cdOrder.countDown();
long beginTime = System.currentTimeMillis();
System.out.println("線程" + Thread.currentThread().getName() +
"已經發送命令,正在等待結果");
cdAnswer.await();
System.out.println("線程" + Thread.currentThread().getName() +
"已收到所有響應結果,所用時間為:" + (System.currentTimeMillis()-beginTime));
}catch(Exception e){
e.printStackTrace();
}
service.shutdown();
}
}
class SynchonizedClass{
public synchronized void start() throws InterruptedException{
Thread.sleep(100);//執行其它邏輯消耗時間
// synchronized(this){
System.out.println("我運行使用了 10 ms");
// }
}
}
運行結果如下:
線程main發布執行命令
線程main已經發送命令,正在等待結果
我運行使用了 10 ms
我運行使用了 10 ms
我運行使用了 10 ms
線程main已收到所有響應結果,所用時間為:332
兩者相差:222ms。
下面我們着重介紹java中的 Sychronized的用法,具體為:同步方法 與 同步塊
synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對類成員變量的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類實例,其所有聲明為 synchronized 的成員函數中至多只有一個處於可執行狀態(因為至多只有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。
在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明為static synchronized ,以控制其對類的靜態成員變量的訪問。
synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明為 synchronized ,並在主方法中調用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。
2. synchronized 塊:通過 synchronized關鍵字來聲明synchronized 塊。語法如下:
synchronized(syncObject) {
//允許訪問控制的代碼
}
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。
注意:
在使用synchronized關鍵字時候,應該盡可能避免在synchronized方法或synchronized塊中使用sleep或者yield方法,因為synchronized程序塊占有着對象鎖,你休息那么其他的線程只能一邊等着你醒來執行完了才能執行。不但嚴重影響效率,也不合邏輯。
同樣,在同步程序塊內調用yeild方法讓出CPU資源也沒有意義,因為你占用着鎖,其他互斥線程還是無法訪問同步程序塊。當然與同步程序塊無關的線程可以獲得更多的執行時間。
總結:
基礎測試類:
package com.quant.dev.modules.dev.enetity;
/**
* @program: dev
* @description:
* @author: Mr.EternityZhang
* @create: 2019-06-24 17:07
*/
public class TestSync {
//代碼塊
public void test1()
{
synchronized(this)
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
}
//同步方法
public synchronized void test2()
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
//同步方法
public synchronized void test3()
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
//同步靜態方法
public static synchronized void test4()
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
//普通方法
public void test5()
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
}
1、synchronized關鍵字的作用域有二種:
1)是某個對象實例內,synchronized aMethod(){}可以防止多個線程同時訪問這個對象的synchronized方法(如果一個對象有多個synchronized方法,只要一個線程訪問了其中的一個synchronized方法,其它線程不能同時訪問這個對象中任何一個synchronized方法)。這時,不同的對象實例的synchronized方法是不相干擾的。也就是說,其它線程照樣可以同時訪問相同類的另一個對象實例中的synchronized方法;
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test2(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt1.test3(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
印證:前提同一個對象,如果一個線程獲取了鎖,另外一個線程無法訪問其他的synchronized方法,直至鎖釋放
改變main方法:
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test2(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt2.test3(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test2 : 4
test1 : 4
test1 : 3
test2 : 3
test2 : 2
test1 : 2
test2 : 1
test1 : 1
test2 : 0
test1 : 0
印證:其它線程照樣可以同時訪問相同類的 另一個對象實例 中的synchronized方法;同時證明了synchronized是非公平鎖,先開始的線程不一定先執行
2)是某個類的范圍,synchronized static aStaticMethod{}防止多個線程中不同的實例對象(或者同一個實例對象)同時訪問這個類中的synchronized static 方法。它可以對類的所有對象實例起作用。
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test4(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt2.test4(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
印證:不同實例,訪問static synchronized方法,鎖起作用,產生阻塞.
2、除了方法前用synchronized關鍵字,synchronized關鍵字還可以用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問。用法是: synchronized(this){/區塊/}(或者synchronized(obj){/區塊/}),它的作用域是當前對象;
3、synchronized關鍵字是不能繼承的,也就是說,基類的方法synchronized f(){} 在繼承類中並不自動是synchronized f(){},而是變成了f(){}。繼承類需要你顯式的指定它的某個方法為synchronized方法
對synchronized(this)的一些理解(很好的解釋了對象鎖,注意其中的this關鍵字)
一、(前提同一實例對象)當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行,另一個線程必須等待當前線程執行完這個代碼塊以后才能執行該代碼塊.同時,一個線程訪問synchronized(this)同步代碼塊,另外一個線程不能訪問synchronized(this)同步代碼塊,也不能訪問synchronized方法,直至鎖釋放。
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt1.test2(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
二、(前提:同一實例對象或者不同實例都可以)然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊或者非synchronized方法.
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt1.test5(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test2 : 4
test1 : 4
test1 : 3
test2 : 3
test1 : 2
test2 : 2
test1 : 1
test2 : 1
test2 : 0
test1 : 0
三、(前提同一實例對象)尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞,synchronized方法也被阻塞。
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt1.test2(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
四、(前提同一實例對象)第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。注意:static synchronized方法不受影響,仍然可以執行
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt1.test2(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
public static void main(String[] args)
{
final TestSync myt1 = new TestSync();
final TestSync myt2 = new TestSync();
Thread test1 = new Thread( new Runnable() { public void run() { myt1.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt1.test4(); } }, "test2" );
test1.start();
test2.start();
}
執行結果:
test1 : 4
test2 : 4
test2 : 3
test1 : 3
test2 : 2
test1 : 2
test2 : 1
test1 : 1
test2 : 0
test1 : 0
五、synchronized修飾方法和用synchronized(this)獲取的是該對象的鎖,兩者效果一樣
六、synchronized(非this對象)同步代碼塊
好處: 如果在一個類中有很多個synchronized方法,這是雖然可以實現同步,但是會受到阻塞。影響效率。但是如果使用了同步代碼塊的非this鎖,則該synchronized(非this)代碼塊和程序中的synchronized同步方法是異步的。不和其他線程爭取this鎖,可以提高運行效率。
使用synchronized(任意自定義對象)進行同步操作,對象監視器必須是同一個對象。如果不是同一個,運行就是異步執行了。
七、synchronized (類.class)
一個類只有一個類對象,其實類鎖也是類的對象鎖,靜態的方法或代碼塊是屬於類(類對象)的,所以synchronized修飾靜態資源需要獲取類鎖。
static修飾的靜態方法 和 static修飾靜態方法內有synchronized代碼塊獲取的也是類鎖
synchronized (類.class)修飾非靜態代碼塊,所有類的實例包括類對象本身(類.class)都會被堵塞。
終極總結(已驗證)
以單實例還是多實例區分
同一實例前提下:
synchronized(this)同步代碼塊/synchronized方法/static synchronized方法執行的時候,普通方法都可以執行,不存在互斥行為;
synchronized(this)同步代碼塊/synchronized方法存在互斥;
synchronized(this)同步代碼塊/synchronized(this)同步代碼塊存在互斥;
synchronized(this)同步代碼塊/static synchronized方法不存在互斥;
synchronized方法/static synchronized方法不存在互斥;
synchronized方法/synchronized方法存在互斥;
static synchronized方法與static synchronized方法存在互斥;
synchronized(***.class)與static synchronized方法效果一樣,鎖類
synchronized修飾方法和用synchronized(this)獲取的是該對象的鎖
不同實例:
實例a的synchronized(this)同步代碼塊/synchronized方法/static synchronized方法執行的時候,與實例b的普通方法都可以執行,不存在互斥行為;
實例a的synchronized(this)同步代碼塊與實例b的synchronized方法不存在互斥;
實例a的synchronized(this)同步代碼塊/實例b的synchronized(this)同步代碼塊不存在互斥;
實例a的synchronized(this)同步代碼塊與實例b的static synchronized方法不存在互斥;
實例a的synchronized方法與實例b的static synchronized方法不存在互斥;
實例a的synchronized方法與實例b的synchronized方法不存在互斥;
實例a的static synchronized方法與實例b的static synchronized方法存在互斥;
synchronized(***.class)與static synchronized方法效果一樣,鎖類
synchronized修飾方法和用synchronized(this)獲取的是該對象的鎖
以synchronized方法(synchronized(this)與synchronized方法相同)與static synchronized方法區分
synchronized方法與synchronized(this)主要針對方法起作用,單實例互斥,多實例不互斥
static synchronized方法針對類起作用,無論單實例還是多實例都互斥
synchronized(***.class)與static synchronized方法效果一樣,鎖類
synchronized修飾方法和用synchronized(this)獲取的是該對象的鎖
別人的總結,可參考
1、synchronized 關鍵字主要用來解決多線程並發同步問題,可以用來修飾類的實例方法、靜態方法、代碼塊;
2、synchronized 實例方法實際保護的是同一個對象的方法調用,當為不同對象時多線程是可以同時訪問同一個 synchronized 方法的;
3、synchronized 靜態方法和 synchronized 實例方法保護的是不同對象,不同的兩個線程可以同時執行 synchronized 靜態方法,另一個執行 synchronized 實例方法,因為 synchronized 靜態方法保護的是 class 類對象,synchronized 實例方法保護的是 this 實例對象;
4、synchronized 代碼塊同步的可以是任何對象,因為任何對象都有一個鎖和等待隊列。
5、synchronized 具備可重入性,對同一個線程在獲得鎖之后在調用其他需要同樣鎖的代碼時可以直接調用,其可重入性是通過記錄鎖的持有線程和持有數量來實現的,調用 synchronized 代碼時檢查對象是否已經被鎖,是則檢查是否被當前線程鎖定,是則計數加一,不是則加入等待隊列,釋放時計數減一直到為零釋放鎖。
6、synchronized 還具備內存可見性,除了實現原子操作避免競態以外對於明顯是原子操作的方法(譬如一個 boolean 狀態變量 state 的 get 和 set 方法)也可以通過 synchronized 來保證並發的可見性,在釋放鎖時所有寫入都會寫回內存,而獲得鎖后都會從內存讀取最新數據;不過對於已經是原子性的操作為了保證內存可見性而使用 synchronized 的成本會比較高,輕量級的選擇應該是使用 volatile 修飾,一旦修飾 java 就會在操作對應變量時插入特殊指令保證可見性。
7、synchronized 是重量級鎖,其語義底層是通過一個 monitor 監視器對象來完成,其實 wait、notify 等方法也依賴於 monitor 對象,所以這就是為什么只有在同步的塊或者方法中才能調用 wait、notify 等方法,否則會拋出 IllegalMonitorStateException 異常的原因,監視器鎖(monitor)的本質依賴於底層操作系統的互斥鎖(Mutex Lock)實現,而操作系統實現線程之間的切換需要從用戶態轉換到核心態,這個成本非常高,狀態之間的轉換需要相對比較長的時間,所以這就是為什么 synchronized 效率低且重量級的原因(Java 1.6 進行了優化,但是相比其他鎖機制還是略顯偏重)。
8、synchronized 在發生異常時會自動釋放線程占用的鎖資源,Lock 需要在異常時主動釋放,synchronized 在鎖等待狀態下無法響應中斷而 Lock 可以。
---------------------
原文:https://blog.csdn.net/u010647035/article/details/82320571
