Java中synchronized的幾種使用方法
前言:
在 Java 語(yǔ)言中,保證線程安全性的主要手段是加鎖,而 Java 中的鎖主要有兩種:synchronized 和 Lock,我們今天重點(diǎn)來(lái)看一下 synchronized 的幾種用法。
用法簡(jiǎn)介
使用 synchronized 無(wú)需手動(dòng)執(zhí)行加鎖和釋放鎖的操作,我們只需要聲明 synchronized 關(guān)鍵字就可以了,JVM 層面會(huì)幫我們自動(dòng)的進(jìn)行加鎖和釋放鎖的操作。 synchronized 可用于修飾普通方法、靜態(tài)方法和代碼塊,接下來(lái)我們分別來(lái)看。
1、修飾普通方法
synchronized 修飾普通方法的用法如下:
/** * synchronized 修飾普通方法 */ public synchronized void method() { // .... }
當(dāng) synchronized 修飾普通方法時(shí),被修飾的方法被稱為同步方法,其作用范圍是整個(gè)方法,作用的對(duì)象是調(diào)用這個(gè)方法的對(duì)象。
2、修飾靜態(tài)方法
synchronized 修飾靜態(tài)方法和修飾普通方法類似,它的用法如下:
/** * synchronized 修飾靜態(tài)方法 */ public static synchronized void staticMethod() { // ....... }
當(dāng) synchronized 修飾靜態(tài)方法時(shí),其作用范圍是整個(gè)程序,這個(gè)鎖對(duì)于所有調(diào)用這個(gè)鎖的對(duì)象都是互斥的。
所謂的互斥,指的是同一時(shí)間只能有一個(gè)線程能使用,其他線程只能排隊(duì)等待。
修飾普通方法 VS 修飾靜態(tài)方法
synchronized 修飾普通方法和靜態(tài)方法看似相同,但二者完全不同,對(duì)于靜態(tài)方法來(lái)說(shuō) synchronized 加鎖是全局的,也就是整個(gè)程序運(yùn)行期間,所有調(diào)用這個(gè)靜態(tài)方法的對(duì)象都是互斥的,而普通方法是針對(duì)對(duì)象級(jí)別的,不同的對(duì)象對(duì)應(yīng)著不同的鎖,比如以下代碼,同樣是調(diào)用兩次方法,但鎖的獲取完全不同
實(shí)現(xiàn)代碼如下:
import java.time.LocalDateTime; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class SynchronizedUsage { public static void main(String[] args) throws InterruptedException { // 創(chuàng)建線程池同時(shí)執(zhí)行任務(wù) ExecutorService threadPool = Executors.newFixedThreadPool(10); // 執(zhí)行兩次靜態(tài)方法 threadPool.execute(() -> { staticMethod(); }); threadPool.execute(() -> { staticMethod(); }); // 執(zhí)行兩次普通方法 threadPool.execute(() -> { SynchronizedUsage usage = new SynchronizedUsage(); usage.method(); }); threadPool.execute(() -> { SynchronizedUsage usage2 = new SynchronizedUsage(); usage2.method(); }); } /** * synchronized 修飾普通方法 * 本方法的執(zhí)行需要 3s(因?yàn)橛?3s 的休眠時(shí)間) */ public synchronized void method() { System.out.println("普通方法執(zhí)行時(shí)間:" + LocalDateTime.now()); try { // 休眠 3s TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } /** * synchronized 修飾靜態(tài)方法 * 本方法的執(zhí)行需要 3s(因?yàn)橛?3s 的休眠時(shí)間) */ public static synchronized void staticMethod() { System.out.println("靜態(tài)方法執(zhí)行時(shí)間:" + LocalDateTime.now()); try { // 休眠 3s TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } }
以上程序的執(zhí)行結(jié)果如下:
從上述結(jié)果可以看出,靜態(tài)方法加鎖是全局的,針對(duì)的是所有調(diào)用者;而普通方法加鎖是對(duì)象級(jí)別的,不同的對(duì)象擁有的鎖也不同。
3、修飾代碼塊
我們?cè)谌粘i_(kāi)發(fā)中,最常用的是給代碼塊加鎖,而不是給方法加鎖,因?yàn)榻o方法加鎖,相當(dāng)于給整個(gè)方法全部加鎖,這樣的話鎖的粒度就太大了,程序的執(zhí)行性能就會(huì)受到影響,所以通常情況下,我們會(huì)使用 synchronized 給代碼塊加鎖,
它的實(shí)現(xiàn)語(yǔ)法如下:
public void classMethod() throws InterruptedException { // 前置代碼... // 加鎖代碼 synchronized (SynchronizedUsage.class) { // ...... } // 后置代碼... }
從上述代碼我們可以看出,相比于修飾方法,修飾代碼塊需要自己手動(dòng)指定加鎖對(duì)象,加鎖的對(duì)象通常使用 this 或 xxx.class 這樣的形式來(lái)表示,比如以下代碼:
// 加鎖某個(gè)類 synchronized (SynchronizedUsage.class) { // ...... } // 加鎖當(dāng)前類對(duì)象 synchronized (this) { // ...... }
this VS class
使用 synchronized 加鎖 this 和 xxx.class 是完全不同的,當(dāng)加鎖 this 時(shí),表示用當(dāng)前的對(duì)象進(jìn)行加鎖,每個(gè)對(duì)象都對(duì)應(yīng)了一把鎖;而當(dāng)使用 xxx.class 加鎖時(shí),表示使用某個(gè)類(而非類實(shí)例)來(lái)加鎖,它是應(yīng)用程序級(jí)別的,是全局生效的,
如以下代碼所示:
import java.time.LocalDateTime; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class SynchronizedUsageBlock { public static void main(String[] args) throws InterruptedException { // 創(chuàng)建線程池同時(shí)執(zhí)行任務(wù) ExecutorService threadPool = Executors.newFixedThreadPool(10); // 執(zhí)行兩次 synchronized(this) threadPool.execute(() -> { SynchronizedUsageBlock usage = new SynchronizedUsageBlock(); usage.thisMethod(); }); threadPool.execute(() -> { SynchronizedUsageBlock usage2 = new SynchronizedUsageBlock(); usage2.thisMethod(); }); // 執(zhí)行兩次 synchronized(xxx.class) threadPool.execute(() -> { SynchronizedUsageBlock usage3 = new SynchronizedUsageBlock(); usage3.classMethod(); }); threadPool.execute(() -> { SynchronizedUsageBlock usage4 = new SynchronizedUsageBlock(); usage4.classMethod(); }); } /** * synchronized(this) 加鎖 * 本方法的執(zhí)行需要 3s(因?yàn)橛?3s 的休眠時(shí)間) */ public void thisMethod() { synchronized (this) { System.out.println("synchronized(this) 加鎖:" + LocalDateTime.now()); try { // 休眠 3s TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * synchronized(xxx.class) 加鎖 * 本方法的執(zhí)行需要 3s(因?yàn)橛?3s 的休眠時(shí)間) */ public void classMethod() { synchronized (SynchronizedUsageBlock.class) { System.out.println("synchronized(xxx.class) 加鎖:" + LocalDateTime.now()); try { // 休眠 3s TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } } }
以上程序的執(zhí)行結(jié)果如下:
總結(jié)
synchronized 用 3 種用法,用它可以來(lái)修飾普通方法、靜態(tài)方法和代碼塊,其中最常用的是修飾代碼塊,而修飾代碼塊時(shí)需要指定一個(gè)加鎖對(duì)象,這個(gè)加鎖對(duì)象通常使用 this 或 xxx.class 來(lái)表示,當(dāng)使用 this 時(shí),表示使用當(dāng)前對(duì)象來(lái)加鎖,而使用 class 時(shí),表示表示使用某個(gè)類(非類對(duì)象實(shí)例)來(lái)加鎖,它是全局生效的。
到此這篇關(guān)于Java中synchronized的幾種使用方法的文章就介紹到這了,更多相關(guān)synchronized用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JDBC下Idea添加mysql-jar包的詳細(xì)過(guò)程
這篇文章主要介紹了JDBC下Idea添加mysql-jar包的詳細(xì)過(guò)程,添加jar包首先到官網(wǎng)下載jar包,然后idea導(dǎo)入jar包,在就是檢查,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11一文總結(jié)RabbitMQ中的消息確認(rèn)機(jī)制
RabbitMQ消息確認(rèn)機(jī)制指的是在消息傳遞過(guò)程中,發(fā)送方發(fā)送消息后,接收方需要對(duì)消息進(jìn)行確認(rèn),以確保消息被正確地接收和處理,本文為大家整理了RabbitMQ中的消息確認(rèn)機(jī)制,需要的可以參考一下2023-06-06Spring Boot命令行運(yùn)行器的實(shí)現(xiàn)方法
這篇文章主要介紹了Spring Boot命令行運(yùn)行器的實(shí)現(xiàn)方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-10-10SpringBoot favicon Chrome設(shè)置問(wèn)題解決方案
在本篇文章里小編給大家分享的是關(guān)于SpringBoot favicon Chrome設(shè)置問(wèn)題實(shí)例內(nèi)容,小的朋友們可以參考學(xué)習(xí)下。2020-02-02關(guān)于在IDEA中SpringBoot項(xiàng)目中activiti工作流的使用詳解
這篇文章主要介紹了關(guān)于在IDEA中SpringBoot項(xiàng)目中activiti工作流的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Maven忽略單元測(cè)試及打包到Nexus的實(shí)現(xiàn)
我們的工程在打包發(fā)布時(shí)候,通常都需要忽略單元測(cè)試,以免因環(huán)境原因,無(wú)法通過(guò)單元測(cè)試而影響發(fā)布,本文主要介紹了Maven忽略單元測(cè)試及打包到Nexus的實(shí)現(xiàn),感興趣的可以了解一下2024-04-04