欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

透徹理解Java中Synchronized(對象鎖)和Static Synchronized(類鎖)的區(qū)別

 更新時(shí)間:2018年05月15日 11:37:34   作者:cs408  
這篇文章主要介紹了Java中Synchronized(對象鎖)和Static Synchronized(類鎖)的區(qū)別,希望對大家有所幫助,一起跟隨小編過來看看吧

本文講述了Java中Synchronized(對象鎖)和Static Synchronized(類鎖)的區(qū)別。分享給大家供大家參考,具體如下:

Synchronized和Static Synchronized區(qū)別

通過分析這兩個(gè)用法的分析,我們可以理解java中鎖的概念。一個(gè)是實(shí)例鎖(鎖在某一個(gè)實(shí)例對象上,如果該類是單例,那么該鎖也具有全局鎖的概念),一個(gè)是全局鎖(該鎖針對的是類,無論實(shí)例多少個(gè)對象,那么線程都共享該鎖)。實(shí)例鎖對應(yīng)的就是synchronized關(guān)鍵字,而類鎖(全局鎖)對應(yīng)的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。

下面的文章做了很好的總結(jié):

1.synchronized與static synchronized 的區(qū)別

synchronized是對類的當(dāng)前實(shí)例(當(dāng)前對象)進(jìn)行加鎖,防止其他線程同時(shí)訪問該類的該實(shí)例的所有synchronized塊,注意這里是“類的當(dāng)前實(shí)例”, 類的兩個(gè)不同實(shí)例就沒有這種約束了。

那么static synchronized恰好就是要控制類的所有實(shí)例的并發(fā)訪問,static synchronized是限制多線程中該類的所有實(shí)例同時(shí)訪問jvm中該類所對應(yīng)的代碼塊。實(shí)際上,在類中如果某方法或某代碼塊中有 synchronized,那么在生成一個(gè)該類實(shí)例后,該實(shí)例也就有一個(gè)監(jiān)視塊,防止線程并發(fā)訪問該實(shí)例的synchronized保護(hù)塊,而static synchronized則是所有該類的所有實(shí)例公用得一個(gè)監(jiān)視塊,這就是他們兩個(gè)的區(qū)別。也就是說synchronized相當(dāng)于 this.synchronized,而static synchronized相當(dāng)于Something.synchronized.(后面又講解)

一個(gè)日本作者-結(jié)成浩的《java多線程設(shè)計(jì)模式》有這樣的一個(gè)列子:

pulbic class Something(){ 
 public synchronized void isSyncA(){} 
 public synchronized void isSyncB(){} 
 public static synchronized void cSyncA(){} 
 public static synchronized void cSyncB(){} 
} 

那么,假如有Something類的兩個(gè)實(shí)例x與y,那么下列各組方法被多線程同時(shí)訪問的情況是怎樣的?

a. x.isSyncA()與x.isSyncB()
b. x.isSyncA()與y.isSyncA()
c. x.cSyncA()與y.cSyncB()
d. x.isSyncA()與Something.cSyncA()

這里,很清楚的可以判斷:

a,都是對同一個(gè)實(shí)例(x)的synchronized域訪問,因此不能被同時(shí)訪問。(多線程中訪問x的不同synchronized域不能同時(shí)訪問)

如果在多個(gè)線程中訪問x.isSyncA(),因?yàn)槿匀皇菍ν粋€(gè)實(shí)例,且對同一個(gè)方法加鎖,所以多個(gè)線程中也不能同時(shí)訪問。(多線程中訪問x的同一個(gè)synchronized域不能同時(shí)訪問)

b,是針對不同實(shí)例的,因此可以同時(shí)被訪問(對象鎖對于不同的對象實(shí)例沒有鎖的約束)

c,因?yàn)槭莝tatic synchronized,所以不同實(shí)例之間仍然會被限制,相當(dāng)于Something.isSyncA()與 Something.isSyncB()了,因此不能被同時(shí)訪問。

那么,第d呢?,書上的 答案是可以被同時(shí)訪問的,答案理由是synchronzied的是實(shí)例方法與synchronzied的類方法由于鎖定(lock)不同的原因。

個(gè)人分析也就是synchronized 與static synchronized 相當(dāng)于兩幫派,各自管各自,相互之間就無約束了,可以被同時(shí)訪問。

舉個(gè)例子:

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同時(shí)修飾靜態(tài)方法和實(shí)例方法,但是運(yùn)行結(jié)果是交替進(jìn)行的,這證明了類鎖和對象鎖是兩個(gè)不一樣的鎖,控制著不同的區(qū)域,它們是互不干擾的。同樣,線程獲得對象鎖的同時(shí),也可以獲得該類鎖,即同時(shí)獲得兩個(gè)鎖,這是允許的。

結(jié)論:

A: synchronized static是某個(gè)類的范圍,synchronized static cSync{}防止多個(gè)線程中多個(gè)實(shí)例同時(shí)訪問這個(gè) 類中的synchronized static 方法。它可以對類的所有對象實(shí)例起作用。

B: synchronized 是某實(shí)例的范圍,synchronized isSync(){}防止多個(gè)線程中這一個(gè)實(shí)例同時(shí)訪問這個(gè)類的synchronized 方法。

其實(shí)總結(jié)起來很簡單。

  • 一個(gè)鎖的是類對象,一個(gè)鎖的是實(shí)例對象。
  • 若類對象被lock,則類對象的所有同步方法全被lock;
  • 若實(shí)例對象被lock,則該實(shí)例對象的所有同步方法全被lock。

2.synchronized方法與synchronized代碼快的區(qū)別

synchronized methods(){} 與synchronized(this){}之間沒有什么區(qū)別,只是synchronized methods(){} 便于閱讀理解,而synchronized(this){}可以更精確的控制沖突限制訪問區(qū)域,有時(shí)候表現(xiàn)更高效率。

兩種方式效率比較:

1、同步塊,代碼如下:

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() + 
 "發(fā)布執(zhí)行命令"); 
 cdOrder.countDown(); 
 long beginTime = System.currentTimeMillis(); 
 System.out.println("線程" + Thread.currentThread().getName() + 
 "已經(jīng)發(fā)送命令,正在等待結(jié)果"); 
 cdAnswer.await(); 
 System.out.println("線程" + Thread.currentThread().getName() + 
 "已收到所有響應(yīng)結(jié)果,所用時(shí)間為:" + (System.currentTimeMillis()-beginTime)); 
 }catch(Exception e){ 
 e.printStackTrace(); 
 } 
 service.shutdown(); 
 } 
} 
class SynchonizedClass{ 
 public void start() throws InterruptedException{ 
 Thread.sleep(100);//執(zhí)行其它邏輯消耗時(shí)間 
 synchronized(this){ 
 System.out.println("我運(yùn)行使用了 10 ms"); 
 } 
 } 
} 

運(yùn)行結(jié)果如下:

線程main發(fā)布執(zhí)行命令
線程main已經(jīng)發(fā)送命令,正在等待結(jié)果
我運(yùn)行使用了 10 ms
我運(yùn)行使用了 10 ms
我運(yùn)行使用了 10 ms
線程main已收到所有響應(yīng)結(jié)果,所用時(shí)間為:110

同步方法,代碼如下:

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() + 
 "發(fā)布執(zhí)行命令"); 
 cdOrder.countDown(); 
 long beginTime = System.currentTimeMillis(); 
 System.out.println("線程" + Thread.currentThread().getName() + 
 "已經(jīng)發(fā)送命令,正在等待結(jié)果"); 
 cdAnswer.await(); 
 System.out.println("線程" + Thread.currentThread().getName() + 
 "已收到所有響應(yīng)結(jié)果,所用時(shí)間為:" + (System.currentTimeMillis()-beginTime)); 
 }catch(Exception e){ 
 e.printStackTrace(); 
 } 
 service.shutdown(); 
 } 
} 
class SynchonizedClass{ 
 public synchronized void start() throws InterruptedException{ 
 Thread.sleep(100);//執(zhí)行其它邏輯消耗時(shí)間 
// synchronized(this){ 
 System.out.println("我運(yùn)行使用了 10 ms"); 
// } 
 } 
} 

運(yùn)行結(jié)果如下:

線程main發(fā)布執(zhí)行命令
線程main已經(jīng)發(fā)送命令,正在等待結(jié)果
我運(yùn)行使用了 10 ms
我運(yùn)行使用了 10 ms
我運(yùn)行使用了 10 ms
線程main已收到所有響應(yīng)結(jié)果,所用時(shí)間為:332

兩者相差:222ms。

 對比說明同步代碼塊比同步方法效率更高。

補(bǔ)充記憶:

1、synchronized關(guān)鍵字的作用域有二種:

1)是某個(gè)對象實(shí)例內(nèi),synchronized aMethod(){}可以防止多個(gè)線程同時(shí)訪問這個(gè)對象的synchronized方法(如果一個(gè)對象有多個(gè)synchronized方法,只要一個(gè)線程訪問了其中的一個(gè)synchronized方法,其它線程不能同時(shí)訪問這個(gè)對象中任何一個(gè)synchronized方法)。這時(shí),不同的對象實(shí)例的synchronized方法是不相干擾的。也就是說,其它線程照樣可以同時(shí)訪問相同類的另一個(gè)對象實(shí)例中的synchronized方法;

2)是某個(gè)類的范圍,synchronized static aStaticMethod{}防止多個(gè)線程中不同的實(shí)例對象(或者同一個(gè)實(shí)例對象)同時(shí)訪問這個(gè)類中的synchronized static 方法。它可以對類的所有對象實(shí)例起作用。

2、除了方法前用synchronized關(guān)鍵字,synchronized關(guān)鍵字還可以用于方法中的某個(gè)區(qū)塊中,表示只對這個(gè)區(qū)塊的資源實(shí)行互斥訪問。用法是: synchronized(this){/*區(qū)塊*/}(或者synchronized(obj){/*區(qū)塊*/}),它的作用域是當(dāng)前對象;

3、synchronized關(guān)鍵字是不能繼承的,也就是說,基類的方法synchronized f(){} 在繼承類中并不自動(dòng)是synchronized f(){},而是變成了f(){}。繼承類需要你顯式的指定它的某個(gè)方法為synchronized方法; 

對synchronized(this)的一些理解(很好的解釋了對象鎖,注意其中的this關(guān)鍵字)  

一、當(dāng)兩個(gè)并發(fā)線程訪問同一個(gè)對象object中的這個(gè)synchronized(this)同步代碼塊時(shí),一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊?!?/p>

二、然而,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),另一個(gè)線程仍然可以訪問該object中的非synchronized(this)同步代碼塊?! ?/p>

三、尤其關(guān)鍵的是,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。  

四、第三個(gè)例子同樣適用其它同步代碼塊。也就是說,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),它就獲得了這個(gè)object的對象鎖。結(jié)果,其它線程對該object對象所有同步代碼部分的訪問都被暫時(shí)阻塞?! ?/p>

五、以上規(guī)則對其它對象鎖同樣適用

補(bǔ)充一段代碼,方便對synchronized關(guān)鍵字進(jìn)行測試(簡單修改即可)

public class TestSynchronized 
{ 
 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 void main(String[] args) 
 { 
 final TestSynchronized myt2 = new TestSynchronized(); 
 final TestSynchronized myt3 = new TestSynchronized(); 
 Thread test1 = new Thread( new Runnable() { public void run() { myt2.test2(); } }, "test1" ); 
 Thread test2 = new Thread( new Runnable() { public void run() { myt2.test3(); } }, "test3" ); 
 test1.start();; 
 test2.start(); 
 } 
} 

運(yùn)行結(jié)果:

test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test3 : 4
test3 : 3
test3 : 2
test3 : 1
test3 : 0

下面我們著重介紹java中的 Sychronized的用法,具體為:同步方法 與 同步塊synchronized 關(guān)鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊?!?/p>

1. synchronized 方法:通過在方法聲明中加入 synchronized關(guān)鍵字來聲明 synchronized 方法。如: 

public synchronized void accessVal(int newVal);

synchronized 方法控制對類成員變量的訪問:每個(gè)類實(shí)例對應(yīng)一把鎖,每個(gè) synchronized 方法都必須獲得調(diào)用該方法的類實(shí)例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨(dú)占該鎖,直到從該方法返回時(shí)才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。這種機(jī)制確保了同一時(shí)刻對于每一個(gè)類實(shí)例,其所有聲明為 synchronized 的成員函數(shù)中至多只有一個(gè)處于可執(zhí)行狀態(tài)(因?yàn)橹炼嘀挥幸粋€(gè)能夠獲得該類實(shí)例對應(yīng)的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。

在 Java 中,不光是類實(shí)例,每一個(gè)類也對應(yīng)一把鎖,這樣我們也可將類的靜態(tài)成員函數(shù)聲明為static  synchronized ,以控制其對類的靜態(tài)成員變量的訪問。

synchronized 方法的缺陷:若將一個(gè)大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由于在線程的整個(gè)生命期內(nèi)它一直在運(yùn)行,因此將導(dǎo)致它對本類任何 synchronized 方法的調(diào)用都永遠(yuǎn)不會成功。當(dāng)然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明為 synchronized ,并在主方法中調(diào)用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊?! ?/p>

2. synchronized 塊:通過 synchronized關(guān)鍵字來聲明synchronized 塊。語法如下:  

synchronized(syncObject) {
  //允許訪問控制的代碼
  }

synchronized 塊是這樣一個(gè)代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實(shí)例或類)的鎖方能執(zhí)行,具體機(jī)制同前所述。由于可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。

注意:

在使用synchronized關(guān)鍵字時(shí)候,應(yīng)該盡可能避免在synchronized方法或synchronized塊中使用sleep或者yield方法,因?yàn)閟ynchronized程序塊占有著對象鎖,你休息那么其他的線程只能一邊等著你醒來執(zhí)行完了才能執(zhí)行。不但嚴(yán)重影響效率,也不合邏輯。

同樣,在同步程序塊內(nèi)調(diào)用yeild方法讓出CPU資源也沒有意義,因?yàn)槟阏加弥i,其他互斥線程還是無法訪問同步程序塊。當(dāng)然與同步程序塊無關(guān)的線程可以獲得更多的執(zhí)行時(shí)間。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java編程之基于SpringBoot框架實(shí)現(xiàn)掃碼登錄

    java編程之基于SpringBoot框架實(shí)現(xiàn)掃碼登錄

    本文將介紹基于SpringBoot + Vue + Android實(shí)現(xiàn)的掃碼登錄demo的總體思路,文中附含詳細(xì)示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-09-09
  • Intellij IDEA如何設(shè)置代理

    Intellij IDEA如何設(shè)置代理

    這篇文章主要介紹了Intellij IDEA如何設(shè)置代理問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • MyBatis注解開發(fā)之實(shí)現(xiàn)自定義映射關(guān)系和關(guān)聯(lián)查詢

    MyBatis注解開發(fā)之實(shí)現(xiàn)自定義映射關(guān)系和關(guān)聯(lián)查詢

    本文主要詳細(xì)介紹了MyBatis注解開發(fā)中,實(shí)現(xiàn)自定義映射關(guān)系和關(guān)聯(lián)查詢,文中有詳細(xì)的代碼示例,對學(xué)習(xí)MyBatis有一定的參考價(jià)值,需要的朋友可以參考閱讀
    2023-04-04
  • java如何生成登錄隨機(jī)驗(yàn)證碼

    java如何生成登錄隨機(jī)驗(yàn)證碼

    這篇文章主要為大家詳細(xì)介紹了java如何生成登錄隨機(jī)驗(yàn)證碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • ssm實(shí)現(xiàn)分頁查詢的實(shí)例

    ssm實(shí)現(xiàn)分頁查詢的實(shí)例

    下面小編就為大家?guī)硪黄猻sm實(shí)現(xiàn)分頁查詢的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-11-11
  • Java多線程案例之單例模式懶漢+餓漢+枚舉

    Java多線程案例之單例模式懶漢+餓漢+枚舉

    這篇文章主要介紹了Java多線程案例之單例模式懶漢+餓漢+枚舉,文章著重介紹在多線程的背景下簡單的實(shí)現(xiàn)單例模式,需要的小伙伴可以參考一下
    2022-06-06
  • Eclipse創(chuàng)建JavaWeb工程的完整步驟記錄

    Eclipse創(chuàng)建JavaWeb工程的完整步驟記錄

    很多新手不知道Eclipse怎么創(chuàng)建Java Web項(xiàng)目,一起來看看吧,這篇文章主要給大家介紹了關(guān)于Eclipse創(chuàng)建JavaWeb工程的完整步驟,需要的朋友可以參考下
    2023-10-10
  • Java?二維數(shù)組創(chuàng)建及使用方式

    Java?二維數(shù)組創(chuàng)建及使用方式

    這篇文章主要介紹了Java?二維數(shù)組創(chuàng)建及使用方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 詳解Spring bean的注解注入之@Autowired的原理及使用

    詳解Spring bean的注解注入之@Autowired的原理及使用

    之前講過bean注入是什么,也使用了xml的配置文件進(jìn)行bean注入,這也是Spring的最原始的注入方式(xml注入).本文主要講解的注解有以下幾個(gè):@Autowired、 @Service、@Repository、@Controller 、@Component、@Bean、@Configuration、@Resource ,需要的朋友可以參考下
    2021-06-06
  • javaWeb如何實(shí)現(xiàn)隨機(jī)圖片驗(yàn)證碼詳解

    javaWeb如何實(shí)現(xiàn)隨機(jī)圖片驗(yàn)證碼詳解

    這篇文章主要給大家介紹了關(guān)于javaWeb如何實(shí)現(xiàn)隨機(jī)圖片驗(yàn)證碼的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03

最新評論