乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      java synchronized類鎖,對象鎖詳解(轉(zhuǎn)載)

       Levy_X 2017-07-25

      覺得還不錯 留個記錄,轉(zhuǎn)載自http://zhh9106./blog/2151791

       

      在java編程中,經(jīng)常需要用到同步,而用得最多的也許是synchronized關(guān)鍵字了,下面看看這個關(guān)鍵字的用法。

      因為synchronized關(guān)鍵字涉及到鎖的概念,所以先來了解一些相關(guān)的鎖知識。

       

      java的內(nèi)置鎖:每個java對象都可以用做一個實現(xiàn)同步的鎖,這些鎖成為內(nèi)置鎖。線程進入同步代碼塊或方法的時候會自動獲得該鎖,在退出同步代碼塊或方法時會釋放該鎖。獲得內(nèi)置鎖的唯一途徑就是進入這個鎖的保護的同步代碼塊或方法。

       

      java內(nèi)置鎖是一個互斥鎖,這就是意味著最多只有一個線程能夠獲得該鎖,當線程A嘗試去獲得線程B持有的內(nèi)置鎖時,線程A必須等待或者阻塞,知道線程B釋放這個鎖,如果B線程不釋放這個鎖,那么A線程將永遠等待下去。

       

      java的對象鎖和類鎖:java的對象鎖和類鎖在鎖的概念上基本上和內(nèi)置鎖是一致的,但是,兩個鎖實際是有很大的區(qū)別的,對象鎖是用于對象實例方法,或者一個對象實例上的,類鎖是用于類的靜態(tài)方法或者一個類的class對象上的。我們知道,類的對象實例可以有很多個,但是每個類只有一個class對象,所以不同對象實例的對象鎖是互不干擾的,但是每個類只有一個類鎖。但是有一點必須注意的是,其實類鎖只是一個概念上的東西,并不是真實存在的,它只是用來幫助我們理解鎖定實例方法和靜態(tài)方法的區(qū)別的

       

      上面已經(jīng)對鎖的一些概念有了一點了解,下面探討synchronized關(guān)鍵字的用法。

       

      synchronized的用法:synchronized修飾方法和synchronized修飾代碼塊。

       

      下面分別分析這兩種用法在對象鎖和類鎖上的效果。

       

      對象鎖的synchronized修飾方法和代碼塊:

      Java代碼   收藏代碼
      1. public class TestSynchronized   
      2. {    
      3.     public void test1()   
      4.     {    
      5.          synchronized(this)   
      6.          {    
      7.               int i = 5;    
      8.               while( i-- > 0)   
      9.               {    
      10.                    System.out.println(Thread.currentThread().getName()   ' : '   i);    
      11.                    try   
      12.                    {    
      13.                         Thread.sleep(500);    
      14.                    }   
      15.                    catch (InterruptedException ie)   
      16.                    {    
      17.                    }    
      18.               }    
      19.          }    
      20.     }    
      21.       
      22.     public synchronized void test2()   
      23.     {    
      24.          int i = 5;    
      25.          while( i-- > 0)   
      26.          {    
      27.               System.out.println(Thread.currentThread().getName()   ' : '   i);    
      28.               try   
      29.               {    
      30.                    Thread.sleep(500);    
      31.               }   
      32.               catch (InterruptedException ie)   
      33.               {    
      34.               }    
      35.          }    
      36.     }    
      37.       
      38.     public static void main(String[] args)   
      39.     {    
      40.          final TestSynchronized myt2 = new TestSynchronized();    
      41.          Thread test1 = new Thread(  new Runnable() {  public void run() {  myt2.test1();  }  }, 'test1'  );    
      42.          Thread test2 = new Thread(  new Runnable() {  public void run() { myt2.test2();   }  }, 'test2'  );    
      43.          test1.start();;    
      44.          test2.start();    
      45. //         TestRunnable tr=new TestRunnable();  
      46. //         Thread test3=new Thread(tr);  
      47. //         test3.start();  
      48.     }   
      49.     
      50. }  

       

       

      Java代碼   收藏代碼
      1. test2 : 4  
      2. test2 : 3  
      3. test2 : 2  
      4. test2 : 1  
      5. test2 : 0  
      6. test1 : 4  
      7. test1 : 3  
      8. test1 : 2  
      9. test1 : 1  
      10. test1 : 0  

       

      上述的代碼,第一個方法時用了同步代碼塊的方式進行同步,傳入的對象實例是this,表明是當前對象,當然,如果需要同步其他對象實例,也不可傳入其他對象的實例;第二個方法是修飾方法的方式進行同步。因為第一個同步代碼塊傳入的this,所以兩個同步代碼所需要獲得的對象鎖都是同一個對象鎖,下面main方法時分別開啟兩個線程,分別調(diào)用test1和test2方法,那么兩個線程都需要獲得該對象鎖,另一個線程必須等待。上面也給出了運行的結(jié)果可以看到:直到test2線程執(zhí)行完畢,釋放掉鎖,test1線程才開始執(zhí)行。(可能這個結(jié)果有人會有疑問,代碼里面明明是先開啟test1線程,為什么先執(zhí)行的是test2呢?這是因為java編譯器在編譯成字節(jié)碼的時候,會對代碼進行一個重排序,也就是說,編譯器會根據(jù)實際情況對代碼進行一個合理的排序,編譯前代碼寫在前面,在編譯后的字節(jié)碼不一定排在前面,所以這種運行結(jié)果是正常的, 這里是題外話,最主要是檢驗synchronized的用法的正確性)

       

      如果我們把test2方法的synchronized關(guān)鍵字去掉,執(zhí)行結(jié)果會如何呢?

      Java代碼   收藏代碼
      1. test1 : 4  
      2. test2 : 4  
      3. test2 : 3  
      4. test1 : 3  
      5. test1 : 2  
      6. test2 : 2  
      7. test2 : 1  
      8. test1 : 1  
      9. test2 : 0  
      10. test1 : 0  

       

      上面是執(zhí)行結(jié)果,我們可以看到,結(jié)果輸出是交替著進行輸出的,這是因為,某個線程得到了對象鎖,但是另一個線程還是可以訪問沒有進行同步的方法或者代碼。進行了同步的方法(加鎖方法)和沒有進行同步的方法(普通方法)是互不影響的,一個線程進入了同步方法,得到了對象鎖,其他線程還是可以訪問那些沒有同步的方法(普通方法)。這里涉及到內(nèi)置鎖的一個概念(此概念出自java并發(fā)編程實戰(zhàn)第二章):對象的內(nèi)置鎖和對象的狀態(tài)之間是沒有內(nèi)在的關(guān)聯(lián)的,雖然大多數(shù)類都將內(nèi)置鎖用做一種有效的加鎖機制,但對象的域并不一定通過內(nèi)置鎖來保護。當獲取到與對象關(guān)聯(lián)的內(nèi)置鎖時,并不能阻止其他線程訪問該對象,當某個線程獲得對象的鎖之后,只能阻止其他線程獲得同一個鎖。之所以每個對象都有一個內(nèi)置鎖,是為了免去顯式地創(chuàng)建鎖對象。

       

      所以synchronized只是一個內(nèi)置鎖的加鎖機制,當某個方法加上synchronized關(guān)鍵字后,就表明要獲得該內(nèi)置鎖才能執(zhí)行,并不能阻止其他線程訪問不需要獲得該內(nèi)置鎖的方法。

       

      類鎖的修飾(靜態(tài))方法和代碼塊:

      Java代碼   收藏代碼
      1. public class TestSynchronized   
      2. {    
      3.     public void test1()   
      4.     {    
      5.          synchronized(TestSynchronized.class)   
      6.          {    
      7.               int i = 5;    
      8.               while( i-- > 0)   
      9.               {    
      10.                    System.out.println(Thread.currentThread().getName()   ' : '   i);    
      11.                    try   
      12.                    {    
      13.                         Thread.sleep(500);    
      14.                    }   
      15.                    catch (InterruptedException ie)   
      16.                    {    
      17.                    }    
      18.               }    
      19.          }    
      20.     }    
      21.       
      22.     public static synchronized void test2()   
      23.     {    
      24.          int i = 5;    
      25.          while( i-- > 0)   
      26.          {    
      27.               System.out.println(Thread.currentThread().getName()   ' : '   i);    
      28.               try   
      29.               {    
      30.                    Thread.sleep(500);    
      31.               }   
      32.               catch (InterruptedException ie)   
      33.               {    
      34.               }    
      35.          }    
      36.     }    
      37.       
      38.     public static void main(String[] args)   
      39.     {    
      40.          final TestSynchronized myt2 = new TestSynchronized();    
      41.          Thread test1 = new Thread(  new Runnable() {  public void run() {  myt2.test1();  }  }, 'test1'  );    
      42.          Thread test2 = new Thread(  new Runnable() {  public void run() { TestSynchronized.test2();   }  }, 'test2'  );    
      43.          test1.start();    
      44.          test2.start();    
      45. //         TestRunnable tr=new TestRunnable();  
      46. //         Thread test3=new Thread(tr);  
      47. //         test3.start();  
      48.     }   
      49.     
      50. }  

       

      Java代碼   收藏代碼
      1. test1 : 4  
      2. test1 : 3  
      3. test1 : 2  
      4. test1 : 1  
      5. test1 : 0  
      6. test2 : 4  
      7. test2 : 3  
      8. test2 : 2  
      9. test2 : 1  
      10. test2 : 0  

       

      其實,類鎖修飾方法和代碼塊的效果和對象鎖是一樣的,因為類鎖只是一個抽象出來的概念,只是為了區(qū)別靜態(tài)方法的特點,因為靜態(tài)方法是所有對象實例共用的,所以對應(yīng)著synchronized修飾的靜態(tài)方法的鎖也是唯一的,所以抽象出來個類鎖。其實這里的重點在下面這塊代碼,synchronized同時修飾靜態(tài)和非靜態(tài)方法

      Java代碼   收藏代碼
      1. public class TestSynchronized   
      2. {    
      3.     public synchronized void test1()   
      4.     {    
      5.               int i = 5;    
      6.               while( i-- > 0)   
      7.               {    
      8.                    System.out.println(Thread.currentThread().getName()   ' : '   i);    
      9.                    try   
      10.                    {    
      11.                         Thread.sleep(500);    
      12.                    }   
      13.                    catch (InterruptedException ie)   
      14.                    {    
      15.                    }    
      16.               }    
      17.     }    
      18.       
      19.     public static synchronized void test2()   
      20.     {    
      21.          int i = 5;    
      22.          while( i-- > 0)   
      23.          {    
      24.               System.out.println(Thread.currentThread().getName()   ' : '   i);    
      25.               try   
      26.               {    
      27.                    Thread.sleep(500);    
      28.               }   
      29.               catch (InterruptedException ie)   
      30.               {    
      31.               }    
      32.          }    
      33.     }    
      34.       
      35.     public static void main(String[] args)   
      36.     {    
      37.          final TestSynchronized myt2 = new TestSynchronized();    
      38.          Thread test1 = new Thread(  new Runnable() {  public void run() {  myt2.test1();  }  }, 'test1'  );    
      39.          Thread test2 = new Thread(  new Runnable() {  public void run() { TestSynchronized.test2();   }  }, 'test2'  );    
      40.          test1.start();    
      41.          test2.start();    
      42. //         TestRunnable tr=new TestRunnable();  
      43. //         Thread test3=new Thread(tr);  
      44. //         test3.start();  
      45.     }   
      46.     
      47. }  

       

      Java代碼   收藏代碼
      1. test1 : 4  
      2. test2 : 4  
      3. test1 : 3  
      4. test2 : 3  
      5. test2 : 2  
      6. test1 : 2  
      7. test2 : 1  
      8. test1 : 1  
      9. test1 : 0  
      10. test2 : 0  

       

      上面代碼synchronized同時修飾靜態(tài)方法和實例方法,但是運行結(jié)果是交替進行的,這證明了類鎖和對象鎖是兩個不一樣的鎖,控制著不同的區(qū)域,它們是互不干擾的。同樣,線程獲得對象鎖的同時,也可以獲得該類鎖,即同時獲得兩個鎖,這是允許的。

       

      到這里,對synchronized的用法已經(jīng)有了一定的了解。這時有一個疑問,既然有了synchronized修飾方法的同步方式,為什么還需要synchronized修飾同步代碼塊的方式呢?而這個問題也是synchronized的缺陷所在

       

      synchronized的缺陷:當某個線程進入同步方法獲得對象鎖,那么其他線程訪問這里對象的同步方法時,必須等待或者阻塞,這對高并發(fā)的系統(tǒng)是致命的,這很容易導致系統(tǒng)的崩潰。如果某個線程在同步方法里面發(fā)生了死循環(huán),那么它就永遠不會釋放這個對象鎖,那么其他線程就要永遠的等待。這是一個致命的問題。

       

      當然同步方法和同步代碼塊都會有這樣的缺陷,只要用了synchronized關(guān)鍵字就會有這樣的風險和缺陷。既然避免不了這種缺陷,那么就應(yīng)該將風險降到最低。這也是同步代碼塊在某種情況下要優(yōu)于同步方法的方面。例如在某個類的方法里面:這個類里面聲明了一個對象實例,SynObject so=new SynObject();在某個方法里面調(diào)用了這個實例的方法so.testsy();但是調(diào)用這個方法需要進行同步,不能同時有多個線程同時執(zhí)行調(diào)用這個方法。

      這時如果直接用synchronized修飾調(diào)用了so.testsy();代碼的方法,那么當某個線程進入了這個方法之后,這個對象其他同步方法都不能給其他線程訪問了。假如這個方法需要執(zhí)行的時間很長,那么其他線程會一直阻塞,影響到系統(tǒng)的性能。

      如果這時用synchronized來修飾代碼塊:synchronized(so){so.testsy();},那么這個方法加鎖的對象是so這個對象,跟執(zhí)行這行代碼的對象沒有關(guān)系,當一個線程執(zhí)行這個方法時,這對其他同步方法時沒有影響的,因為他們持有的鎖都完全不一樣。

       

      不過這里還有一種特例,就是上面演示的第一個例子,對象鎖synchronized同時修飾方法和代碼塊,這時也可以體現(xiàn)到同步代碼塊的優(yōu)越性,如果test1方法同步代碼塊后面有非常多沒有同步的代碼,而且有一個100000的循環(huán),這導致test1方法會執(zhí)行時間非常長,那么如果直接用synchronized修飾方法,那么在方法沒執(zhí)行完之前,其他線程是不可以訪問test2方法的,但是如果用了同步代碼塊,那么當退出代碼塊時就已經(jīng)釋放了對象鎖,當線程還在執(zhí)行test1的那個100000的循環(huán)時,其他線程就已經(jīng)可以訪問test2方法了。這就讓阻塞的機會或者線程更少。讓系統(tǒng)的性能更優(yōu)越。

       

      一個類的對象鎖和另一個類的對象鎖是沒有關(guān)聯(lián)的,當一個線程獲得A類的對象鎖時,它同時也可以獲得B類的對象鎖。

       

      可能上面只有理論和代碼,對剛接觸的人比較難理解,下面舉一個例子,

      這個例子是摘自博客:http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html

       

       

      打個比方:一個object就像一個大房子,大門永遠打開。房子里有 很多房間(也就是方法)。

      這些房間有上鎖的(synchronized方法), 和不上鎖之分(普通方法)。房門口放著一把鑰匙(key),這把鑰匙可以打開所有上鎖的房間。

      另外我把所有想調(diào)用該對象方法的線程比喻成想進入這房子某個 房間的人。所有的東西就這么多了,下面我們看看這些東西之間如何作用的。

      在此我們先來明確一下我們的前提條件。該對象至少有一個synchronized方法,否則這個key還有啥意義。當然也就不會有我們的這個主題了。

      一個人想進入某間上了鎖的房間,他來到房子門口,看見鑰匙在那兒(說明暫時還沒有其他人要使用上鎖的 房間)。于是他走上去拿到了鑰匙,并且按照自己 的計劃使用那些房間。注意一點,他每次使用完一次上鎖的房間后會馬上把鑰匙還回去。即使他要連續(xù)使用兩間上鎖的房間,中間他也要把鑰匙還回去,再取回來。

      因此,普通情況下鑰匙的使用原則是:“隨用隨借,用完即還?!?/p>

      這時其他人可以不受限制的使用那些不上鎖的房間,一個人用一間可以,兩個人用一間也可以,沒限制。但是如果當某個人想要進入上鎖的房間,他就要跑到大門口去看看了。有鑰匙當然拿了就走,沒有的話,就只能等了。

      要是很多人在等這把鑰匙,等鑰匙還回來以后,誰會優(yōu)先得到鑰匙?Not guaranteed。象前面例子里那個想連續(xù)使用兩個上鎖房間的家伙,他中間還鑰匙的時候如果還有其他人在等鑰匙,那么沒有任何保證這家伙能再次拿到。 (JAVA規(guī)范在很多地方都明確說明不保證,像Thread.sleep()休息后多久會返回運行,相同優(yōu)先權(quán)的線程那個首先被執(zhí)行,當要訪問對象的鎖被 釋放后處于等待池的多個線程哪個會優(yōu)先得到,等等。我想最終的決定權(quán)是在JVM,之所以不保證,就是因為JVM在做出上述決定的時候,絕不是簡簡單單根據(jù) 一個條件來做出判斷,而是根據(jù)很多條。而由于判斷條件太多,如果說出來可能會影響JAVA的推廣,也可能是因為知識產(chǎn)權(quán)保護的原因吧。SUN給了個不保證 就混過去了。無可厚非。但我相信這些不確定,并非完全不確定。因為計算機這東西本身就是按指令運行的。即使看起來很隨機的現(xiàn)象,其實都是有規(guī)律可尋。學過 計算機的都知道,計算機里隨機數(shù)的學名是偽隨機數(shù),是人運用一定的方法寫出來的,看上去隨機罷了。另外,或許是因為要想弄的確太費事,也沒多大意義,所 以不確定就不確定了吧。)

      再來看看同步代碼塊。和同步方法有小小的不同。

      1.從尺寸上講,同步代碼塊比同步方法小。你可以把同步代碼塊看成是沒上鎖房間里的一塊用帶鎖的屏風隔開的空間。

      2.同步代碼塊還可以人為的指定獲得某個其它對象的key。就像是指定用哪一把鑰匙才能開這個屏風的鎖,你可以用本房的鑰匙;你也可以指定用另一個房子的鑰匙才能開,這樣的話,你要跑到另一棟房子那兒把那個鑰匙拿來,并用那個房子的鑰匙來打開這個房子的帶鎖的屏風。

               記住你獲得的那另一棟房子的鑰匙,并不影響其他人進入那棟房子沒有鎖的房間。

               為什么要使用同步代碼塊呢?我想應(yīng)該是這樣的:首先對程序來講同步的部分很影響運行效率,而一個方法通常是先創(chuàng)建一些局部變量,再對這些變量做一些 操作,如運算,顯示等等;而同步所覆蓋的代碼越多,對效率的影響就越嚴重。因此我們通常盡量縮小其影響范圍。

      如何做?同步代碼塊。我們只把一個方法中該同 步的地方同步,比如運算。

               另外,同步代碼塊可以指定鑰匙這一特點有個額外的好處,是可以在一定時期內(nèi)霸占某個對象的key。還記得前面說過普通情況下鑰匙的使用原則嗎?,F(xiàn)在不是普通情況了。你所取得的那把鑰匙不是永遠不還,而是在退出同步代碼塊時才還。

                還用前面那個想連續(xù)用兩個上鎖房間的家伙打比方。怎樣才能在用完一間以后,繼續(xù)使用另一間呢。用同步代碼塊吧。先創(chuàng)建另外一個線程,做一個同步代碼 塊,把那個代碼塊的鎖指向這個房子的鑰匙。然后啟動那個線程。只要你能在進入那個代碼塊時抓到這房子的鑰匙,你就可以一直保留到退出那個代碼塊。也就是說 你甚至可以對本房內(nèi)所有上鎖的房間遍歷,甚至再sleep(10*60*1000),而房門口卻還有1000個線程在等這把鑰匙呢。很過癮吧。

       

      大概就這么多了。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多