偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

再有人問你synchronized是什么,就把這篇文章發(fā)給他。

開發(fā) 開發(fā)工具
Java語言為了解決并發(fā)編程中存在的原子性、可見性和有序性問題,提供了一系列和并發(fā)處理相關(guān)的關(guān)鍵字,比如synchronized、volatile、final、concurren包等。

 在再有人問你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中我們曾經(jīng)介紹過,Java語言為了解決并發(fā)編程中存在的原子性、可見性和有序性問題,提供了一系列和并發(fā)處理相關(guān)的關(guān)鍵字,比如synchronized、volatile、final、concurren包等。

在《深入理解Java虛擬機(jī)》中,有這樣一段話:

synchronized關(guān)鍵字在需要原子性、可見性和有序性這三種特性的時(shí)候都可以作為其中一種解決方案,看起來是“***”的。的確,大部分并發(fā)控制操作都能使用synchronized來完成。

海明威在他的《午后之死》說過的:“冰山運(yùn)動(dòng)之雄偉壯觀,是因?yàn)樗挥邪朔种辉谒嫔稀?rdquo;

對于程序員來說,synchronized只是個(gè)關(guān)鍵字而已,用起來很簡單。之所以我們可以在處理多線程問題時(shí)可以不用考慮太多,就是因?yàn)檫@個(gè)關(guān)鍵字幫我們屏蔽了很多細(xì)節(jié)。

那么,本文就圍繞synchronized展開,主要介紹其用法、原理,以及如何提供原子性、可見性和有序性保障的等。

synchronized的用法

synchronized是Java提供的一個(gè)并發(fā)控制的關(guān)鍵字。主要有兩種用法,分別是同步方法和同步代碼塊。

也就是說,synchronized既可以修飾方法也可以修飾代碼塊。代碼如下:

  1. /** 
  2.  * @author Hollis 18/08/04. 
  3.  */ 
  4. public class SynchronizedDemo { 
  5.      //同步方法 
  6.     public synchronized void doSth(){ 
  7.         System.out.println("Hello World"); 
  8.     } 
  9.  
  10.     //同步代碼塊 
  11.     public void doSth1(){ 
  12.         synchronized (SynchronizedDemo.class){ 
  13.             System.out.println("Hello World"); 
  14.         } 
  15.     } 

被synchronized修飾的代碼塊及方法,在同一時(shí)間,只能被單個(gè)線程訪問。

synchronized的實(shí)現(xiàn)原理

synchronized,是Java中用于解決并發(fā)情況下數(shù)據(jù)同步訪問的一個(gè)很重要的關(guān)鍵字。當(dāng)我們想要保證一個(gè)共享資源在同一時(shí)間只會被一個(gè)線程訪問到時(shí),我們可以在代碼中使用synchronized關(guān)鍵字對類或者對象加鎖。

在深入理解多線程(一)——Synchronized的實(shí)現(xiàn)原理中我曾經(jīng)介紹過其實(shí)現(xiàn)原理,為了保證知識的完整性,這里再簡單介紹一下,詳細(xì)的內(nèi)容請去原文閱讀。

我們對上面的代碼進(jìn)行反編譯,可以得到如下代碼:

  1. public synchronized void doSth(); 
  2.     descriptor: ()V 
  3.     flags: ACC_PUBLIC, ACC_SYNCHRONIZED 
  4.     Code: 
  5.       stack=2, locals=1, args_size=1 
  6.          0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream; 
  7.          3: ldc           #3                  // String Hello World 
  8.          5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
  9.          8: return 
  10.  
  11.   public void doSth1(); 
  12.     descriptor: ()V 
  13.     flags: ACC_PUBLIC 
  14.     Code: 
  15.       stack=2, locals=3, args_size=1 
  16.          0: ldc           #5                  // class com/hollis/SynchronizedTest 
  17.          2: dup 
  18.          3: astore_1 
  19.          4: monitorenter 
  20.          5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream; 
  21.          8: ldc           #3                  // String Hello World 
  22.         10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
  23.         13: aload_1 
  24.         14: monitorexit 
  25.         15: goto          23 
  26.         18: astore_2 
  27.         19: aload_1 
  28.         20: monitorexit 
  29.         21: aload_2 
  30.         22: athrow 
  31.         23: return 

通過反編譯后代碼可以看出:

對于同步方法,JVM采用ACC_SYNCHRONIZED標(biāo)記符來實(shí)現(xiàn)同步。 

對于同步代碼塊。JVM采用monitorenter、monitorexit兩個(gè)指令來實(shí)現(xiàn)同步。

在The Java? Virtual Machine Specification中有關(guān)于同步方法和同步代碼塊的實(shí)現(xiàn)原理的介紹,我翻譯成中文如下:

方法級的同步是隱式的。同步方法的常量池中會有一個(gè)ACC_SYNCHRONIZED標(biāo)志。當(dāng)某個(gè)線程要訪問某個(gè)方法的時(shí)候,會檢查是否有ACC_SYNCHRONIZED,如果有設(shè)置,則需要先獲得監(jiān)視器鎖,然后開始執(zhí)行方法,方法執(zhí)行之后再釋放監(jiān)視器鎖。這時(shí)如果其他線程來請求執(zhí)行方法,會因?yàn)闊o法獲得監(jiān)視器鎖而被阻斷住。值得注意的是,如果在方法執(zhí)行過程中,發(fā)生了異常,并且方法內(nèi)部并沒有處理該異常,那么在異常被拋到方法外面之前監(jiān)視器鎖會被自動(dòng)釋放。

同步代碼塊使用monitorenter和monitorexit兩個(gè)指令實(shí)現(xiàn)??梢园褕?zhí)行monitorenter指令理解為加鎖,執(zhí)行monitorexit理解為釋放鎖。 每個(gè)對象維護(hù)著一個(gè)記錄著被鎖次數(shù)的計(jì)數(shù)器。未被鎖定的對象的該計(jì)數(shù)器為0,當(dāng)一個(gè)線程獲得鎖(執(zhí)行monitorenter)后,該計(jì)數(shù)器自增變?yōu)?1 ,當(dāng)同一個(gè)線程再次獲得該對象的鎖的時(shí)候,計(jì)數(shù)器再次自增。當(dāng)同一個(gè)線程釋放鎖(執(zhí)行monitorexit指令)的時(shí)候,計(jì)數(shù)器再自減。當(dāng)計(jì)數(shù)器為0的時(shí)候。鎖將被釋放,其他線程便可以獲得鎖。

無論是ACC_SYNCHRONIZED還是monitorenter、monitorexit都是基于Monitor實(shí)現(xiàn)的,在Java虛擬機(jī)(HotSpot)中,Monitor是基于C++實(shí)現(xiàn)的,由ObjectMonitor實(shí)現(xiàn)。

ObjectMonitor類中提供了幾個(gè)方法,如enter、exit、wait、notify、notifyAll等。sychronized加鎖的時(shí)候,會調(diào)用objectMonitor的enter方法,解鎖的時(shí)候會調(diào)用exit方法。(關(guān)于Monitor詳見深入理解多線程(四)—— Moniter的實(shí)現(xiàn)原理)

synchronized與原子性

原子性是指一個(gè)操作是不可中斷的,要全部執(zhí)行完成,要不就都不執(zhí)行。

我們在Java的并發(fā)編程中的多線程問題到底是怎么回事兒中分析過:

線程是CPU調(diào)度的基本單位。CPU有時(shí)間片的概念,會根據(jù)不同的調(diào)度算法進(jìn)行線程調(diào)度。當(dāng)一個(gè)線程獲得時(shí)間片之后開始執(zhí)行,在時(shí)間片耗盡之后,就會失去CPU使用權(quán)。所以在多線程場景下,由于時(shí)間片在線程間輪換,就會發(fā)生原子性問題。

在Java中,為了保證原子性,提供了兩個(gè)高級的字節(jié)碼指令monitorenter和monitorexit。

前面介紹過,這兩個(gè)字節(jié)碼指令,在Java中對應(yīng)的關(guān)鍵字就是synchronized。

通過monitorenter和monitorexit指令,可以保證被synchronized修飾的代碼在同一時(shí)間只能被一個(gè)線程訪問,在鎖未釋放之前,無法被其他線程訪問到。

因此,在Java中可以使用synchronized來保證方法和代碼塊內(nèi)的操作是原子性的。

線程1在執(zhí)行monitorenter指令的時(shí)候,會對Monitor進(jìn)行加鎖,加鎖后其他線程無法獲得鎖,除非線程1主動(dòng)解鎖。即使在執(zhí)行過程中,由于某種原因,比如CPU時(shí)間片用完,線程1放棄了CPU,但是,他并沒有進(jìn)行解鎖。而由于synchronized的鎖是可重入的,下一個(gè)時(shí)間片還是只能被他自己獲取到,還是會繼續(xù)執(zhí)行代碼。直到所有代碼執(zhí)行完。這就保證了原子性。

synchronized與可見性

可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。

我們在再有人問你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中分析過:

Java內(nèi)存模型規(guī)定了所有的變量都存儲在主內(nèi)存中,每條線程還有自己的工作內(nèi)存,線程的工作內(nèi)存中保存了該線程中是用到的變量的主內(nèi)存副本拷貝,線程對變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存。不同的線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量的傳遞均需要自己的工作內(nèi)存和主存之間進(jìn)行數(shù)據(jù)同步進(jìn)行。所以,就可能出現(xiàn)線程1改了某個(gè)變量的值,但是線程2不可見的情況。

前面我們介紹過,被synchronized修飾的代碼,在開始執(zhí)行時(shí)會加鎖,執(zhí)行完成后會進(jìn)行解鎖。

而為了保證可見性,有一條規(guī)則是這樣的:對一個(gè)變量解鎖之前,必須先把此變量同步回主存中。這樣解鎖后,后續(xù)線程就可以訪問到被修改后的值。

所以,synchronized關(guān)鍵字鎖住的對象,其值是具有可見性的。

synchronized與有序性

有序性即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

我們在再有人問你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中分析過:

除了引入了時(shí)間片以外,由于處理器優(yōu)化和指令重排等,CPU還可能對輸入代碼進(jìn)行亂序執(zhí)行,比如load->add->save 有可能被優(yōu)化成load->save->add 。這就是可能存在有序性問題。

這里需要注意的是,synchronized是無法禁止指令重排和處理器優(yōu)化的。也就是說,synchronized無法避免上述提到的問題。

那么,為什么還說synchronized也提供了有序性保證呢?

這就要再把有序性的概念擴(kuò)展一下了。

Java程序中天然的有序性可以總結(jié)為一句話:如果在本線程內(nèi)觀察,所有操作都是天然有序的。如果在一個(gè)線程中觀察另一個(gè)線程,所有操作都是無序的。

以上這句話也是《深入理解Java虛擬機(jī)》中的原句,但是怎么理解呢?周志明并沒有詳細(xì)的解釋。這里我簡單擴(kuò)展一下,這其實(shí)和as-if-serial語義有關(guān)。

as-if-serial語義的意思指:不管怎么重排序(編譯器和處理器為了提高并行度),單線程程序的執(zhí)行結(jié)果都不能被改變。編譯器和處理器無論如何優(yōu)化,都必須遵守as-if-serial語義。

這里不對as-if-serial語義詳細(xì)展開了,簡單說就是,as-if-serial語義保證了單線程中,指令重排是有一定的限制的,而只要編譯器和處理器都遵守了這個(gè)語義,那么就可以認(rèn)為單線程程序是按照順序執(zhí)行的。當(dāng)然,實(shí)際上還是有重排的,只不過我們無須關(guān)心這種重排的干擾。

所以呢,由于synchronized修飾的代碼,同一時(shí)間只能被同一線程訪問。那么也就是單線程執(zhí)行的。所以,可以保證其有序性。

synchronized與鎖優(yōu)化

前面介紹了synchronized的用法、原理以及對并發(fā)編程的作用。是一個(gè)很好用的關(guān)鍵字。

synchronized其實(shí)是借助Monitor實(shí)現(xiàn)的,在加鎖時(shí)會調(diào)用objectMonitor的enter方法,解鎖的時(shí)候會調(diào)用exit方法。事實(shí)上,只有在JDK1.6之前,synchronized的實(shí)現(xiàn)才會直接調(diào)用ObjectMonitor的enter和exit,這種鎖被稱之為重量級鎖。

所以,在JDK1.6中出現(xiàn)對鎖進(jìn)行了很多的優(yōu)化,進(jìn)而出現(xiàn)輕量級鎖,偏向鎖,鎖消除,適應(yīng)性自旋鎖,鎖粗化(自旋鎖在1.4就有,只不過默認(rèn)的是關(guān)閉的,jdk1.6是默認(rèn)開啟的),這些操作都是為了在線程之間更高效的共享數(shù)據(jù) ,解決競爭問題。

好啦,關(guān)于synchronized關(guān)鍵字,我們介紹了其用法、原理、以及如何保證的原子性、順序性和可見性,同時(shí)也擴(kuò)展的留下了鎖優(yōu)化相關(guān)的資料及思考。后面我們會繼續(xù)介紹volatile關(guān)鍵字以及他和synchronized的區(qū)別等。敬請期待。

【本文是51CTO專欄作者Hollis的原創(chuàng)文章,作者微信公眾號Hollis(ID:hollischuang)】

戳這里,看該作者更多好文

 

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2022-09-26 10:09:08

MVCC控制并發(fā)

2020-12-11 08:23:06

JavaMemory Mode內(nèi)存模型

2018-07-03 14:54:25

Java內(nèi)存模型

2018-12-07 09:31:52

分布式鎖服務(wù)框架分布式系統(tǒng)

2021-05-31 09:42:48

MySQL隔離級別

2022-11-08 09:33:36

訂單系統(tǒng)電商

2021-02-22 13:32:19

MySQLSQL索引

2022-05-23 09:41:27

分庫分表數(shù)據(jù)庫算法

2019-11-05 14:06:07

MySQLB+索引

2019-11-04 15:00:50

MySQL索引B+樹

2022-10-21 16:39:56

JDK優(yōu)化

2021-03-08 10:25:37

MySQL數(shù)據(jù)庫索引

2021-03-08 12:47:42

MySQL查詢數(shù)據(jù)

2023-12-11 08:32:58

數(shù)據(jù)庫DruidDBA

2020-04-20 13:11:21

HashMap底層存儲

2020-04-28 09:15:58

HashMapJava數(shù)組

2019-09-19 14:03:32

B樹節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)

2019-12-10 09:08:29

分布式開源RocketMQ

2019-04-15 14:40:46

消息隊(duì)列Java編程

2019-10-31 10:08:15

Synchronize面試線程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號