- Java并發(fā)編程深度解析與實戰(zhàn)
- 譚鋒(Mic)
- 1422字
- 2022-05-10 18:39:16
2.2 Java中的synchronized同步鎖
導(dǎo)致線程安全問題的根本原因在于,存在多個線程同時操作一個共享資源,要想解決這個問題,就需要保證對共享資源訪問的獨占性,因此人們在Java中提供了synchronized關(guān)鍵字,我們稱之為同步鎖,它可以保證在同一時刻,只允許一個線程執(zhí)行某個方法或代碼塊。
synchronized同步鎖具有互斥性,這相當(dāng)于線程由并行執(zhí)行變成串行執(zhí)行,保證了線程的安全性,但是損失了性能。下面我們先來看一下synchronized的使用方法。
2.2.1 synchronized的使用方法
synchronized的使用方法比較簡單,修飾方式有如下兩種。
? 作用在方法級別,表示針對m1()方法加鎖,當(dāng)多個線程同時訪問m1()方法時,同一時刻只有一個線程能執(zhí)行。

? 作用在代碼塊級別,表示針對某一段線程不安全的代碼加鎖,只有訪問到synchronized(this)這行代碼時,才會去競爭鎖資源。

了解了synchronized的基本使用語法之后,我們來看如圖2-4所示的流程,它針對2.1節(jié)的案例增加了synchronized同步鎖之后的執(zhí)行流程。簡單地說,當(dāng)多個線程同時訪問加synchronized關(guān)鍵字修飾的方法時,需要先搶占一個鎖標(biāo)記,只有搶到鎖標(biāo)記的線程才有資格調(diào)用incr()方法。這就使得在同一時刻只有一個線程執(zhí)行i++操作,從而解決了原子性問題。

圖2-4 增加了synchronized同步鎖之后的執(zhí)行流程
2.2.2 了解synchronized同步鎖的作用范圍
我們對一個方法增加synchronized關(guān)鍵字后,當(dāng)多個線程訪問該方法時,整個執(zhí)行過程會變成串行執(zhí)行,這種執(zhí)行方式很明顯會影響程序的性能,那么如何做好安全性及性能的平衡呢?
實際上,synchronized關(guān)鍵字只需要保護(hù)可能存在線程安全問題的代碼,因此,我們可以通過控制同步鎖的作用范圍來實現(xiàn)這個平衡機(jī)制。在synchronized中,提供了兩種鎖,一是類鎖,二是對象鎖。
類鎖
類鎖是全局鎖,當(dāng)多個線程調(diào)用不同對象實例的同步方法時會產(chǎn)生互斥,具體實現(xiàn)方式如下。
? 修飾靜態(tài)方法:

? 修飾代碼塊,synchronized中的鎖對象是類,也就是Lock.class。

下面這段程序使用類鎖來實現(xiàn)跨對象實例,從而實現(xiàn)互斥的功能。

? 該程序中定義了一個m1()方法,該方法中實現(xiàn)了一個循環(huán)打印當(dāng)前線程名稱的邏輯,并且這段邏輯是用類鎖來保護(hù)的。
? 在main()方法中定義了兩個SynchronizedExample對象實例se1和se2,又分別定義了兩個線程來調(diào)用這兩個實例的m1()方法。
根據(jù)類鎖的作用范圍可以知道,即便是多個對象實例,也能夠達(dá)到互斥的目的,因此最終輸出的結(jié)果是:哪個線程搶到了鎖,哪個線程就持續(xù)打印自己的線程名稱。
對象鎖
對象鎖是實例鎖,當(dāng)多個線程調(diào)用同一個對象實例的同步方法時會產(chǎn)生互斥,具體實現(xiàn)方式如下。
? 修飾普通方法:

? 修飾代碼塊,synchronized中的鎖對象是普通對象實例。

下面這段程序演示了對象鎖的使用方法,代碼如下。


我們先來看一下打印結(jié)果。

從以上結(jié)果中我們發(fā)現(xiàn),對于幾乎相同的代碼,在使用對象鎖的情況下,當(dāng)兩個線程分別訪問兩個不同對象實例的m1()方法時,并沒有達(dá)到兩者互斥的目的,看起來似乎鎖沒有生效,實際上并不是鎖沒有生效,問題的根源在于synchronized(lock)中鎖對象lock的作用范圍過小。
Class是在JVM啟動過程中加載的,每個.class文件被裝載后會產(chǎn)生一個Class對象,Class對象在JVM進(jìn)程中是全局唯一的。通過static修飾的成員對象及方法的生命周期都屬于類級別,它們會隨著類的定義被分配和裝載到內(nèi)存,隨著類被卸載而回收。
實例對象的生命周期伴隨著實例對象的創(chuàng)建而開始,同時伴隨著實例對象的回收而結(jié)束。
因此,類鎖和對象鎖最大的區(qū)別是鎖對象lock的生命周期不同,如果要達(dá)到多個線程互斥,那么多個線程必須要競爭同一個對象鎖。
在上述代碼中,通過Object lock=new Object();構(gòu)建的鎖對象的生命周期是由Synchronized-ForObjectExample對象的實例來決定的,不同的SynchronizedForObjectExample實例會有不同的lock鎖對象,由于沒有形成競爭,所以不會實現(xiàn)互斥的效果。如果想要讓上述程序達(dá)到同步的目的,那么我們可以對lock鎖對象增加static關(guān)鍵字。

- LabVIEW Graphical Programming Cookbook
- Learning Chef
- PHP 7底層設(shè)計與源碼實現(xiàn)
- Mastering Entity Framework
- 數(shù)據(jù)庫系統(tǒng)原理及MySQL應(yīng)用教程
- Unity Shader入門精要
- Kinect for Windows SDK Programming Guide
- 前端HTML+CSS修煉之道(視頻同步+直播)
- 區(qū)塊鏈技術(shù)與應(yīng)用
- Unity 3D/2D移動開發(fā)實戰(zhàn)教程
- C語言程序設(shè)計與應(yīng)用(第2版)
- Instant Zurb Foundation 4
- Julia High Performance(Second Edition)
- AngularJS UI Development
- Android Application Programming with OpenCV 3