摘要:基本使用同步代碼塊同步代碼塊延時(shí)秒,方便后面測(cè)試作用代碼塊時(shí),方法中的,是指調(diào)用該方法的對(duì)象。那么這個(gè)時(shí)候使用關(guān)鍵字就需要注意了推薦使用同步代碼塊,同步的代碼塊中傳入外部定義的一個(gè)變量。
簡(jiǎn)述
計(jì)算機(jī)單線程在執(zhí)行任務(wù)時(shí),是嚴(yán)格按照程序的代碼邏輯,按照順序執(zhí)行的。因此單位時(shí)間內(nèi)能執(zhí)行的任務(wù)數(shù)量有限。為了能在相同的時(shí)間內(nèi)能執(zhí)行更多的任務(wù),就必須采用多線程的方式來(lái)執(zhí)行(注意:多線程模式無(wú)法減少單次任務(wù)的執(zhí)行時(shí)間)。但是引入了多線程之后,又帶來(lái)了線程安全的問(wèn)題。而為了解決線程安全的問(wèn)題,又引入了鎖的概念。java中常用的鎖有synchronized和lock兩種,本文我們來(lái)分析synchronized的具體用法和使用注意事項(xiàng)。
基本使用同步代碼塊
/** * 同步代碼塊 * @throws Exception */ public void synchronizedCode() { try { synchronized (this) { System.out.println(getCurrentTime() + ":I am synchronized Code"); Thread.sleep(5000);//延時(shí)5秒,方便后面測(cè)試 } } catch (Exception e) { e.printStackTrace(); } }
作用代碼塊時(shí),synchronized方法中的this,是指調(diào)用該方法的對(duì)象。需要主要的是,synchronized作用代碼塊時(shí),只會(huì)鎖住這一小塊代碼。代碼塊的上下部分的其他代碼在所有的線程仍然是能同時(shí)訪問(wèn)的。同時(shí)需要注意的是每個(gè)對(duì)象有用不同的鎖。即不會(huì)阻塞不同對(duì)象的調(diào)用。
同步方法
/** * 同步方法 */ public synchronized void synchronizedMethod() { try { System.out.println(getCurrentTime() + ":I am synchronized method"); Thread.sleep(5000);//延時(shí)5秒,方便后面測(cè)試 } catch (Exception e) { e.printStackTrace(); } }
synchronized作用在方法上,其實(shí)是缺省了this關(guān)鍵字,實(shí)際上是synchronized(this)。this是指調(diào)用該方法的對(duì)象。此鎖也不會(huì)阻塞不同對(duì)象之間的調(diào)用。
同步靜態(tài)方法
/** * 同步靜態(tài)方法 */ public synchronized static void synchronizedStaticMethod() { try { System.out.println(getCurrentTime() + ":I am synchronized static method"); Thread.sleep(5000);//延時(shí)5秒,方便后面測(cè)試 } catch (Exception e) { e.printStackTrace(); } }
使用方式和作用普通方式相同,唯一需要注意的地方是此鎖所有對(duì)象共用,即不同對(duì)象之間會(huì)阻塞調(diào)用。
測(cè)試準(zhǔn)備簡(jiǎn)單說(shuō)明一下:有一個(gè)線程池,在執(zhí)行多任務(wù)時(shí)使用。每個(gè)同步方法或者代碼塊中都有一個(gè)休眠5秒的動(dòng)作,利用打印時(shí)間加休眠來(lái)看線程之間是否有阻塞效果。然后有一個(gè)1秒打印一次時(shí)間的方法。
public class Synchronized { //打印時(shí)間時(shí)格式化 public static final String timeFormat = "HH:mm:ss"; //執(zhí)行多任務(wù)的線程池 public static final ExecutorService executor = Executors.newFixedThreadPool(4); /** * 同步代碼塊 * @throws Exception */ public void synchronizedCode() { try { synchronized (this) { System.out.println(getCurrentTime() + ":I am synchronized Code"); Thread.sleep(5000);//延時(shí)5秒,方便后面測(cè)試 } } catch (Exception e) { e.printStackTrace(); } } /** * 同步方法 */ public synchronized void synchronizedMethod() { try { System.out.println(getCurrentTime() + ":I am synchronized method"); Thread.sleep(5000);//延時(shí)5秒,方便后面測(cè)試 } catch (Exception e) { e.printStackTrace(); } } /** * 同步靜態(tài)方法 */ public synchronized static void synchronizedStaticMethod() { try { System.out.println(getCurrentTime() + ":I am synchronized static method"); Thread.sleep(5000);//延時(shí)5秒,方便后面測(cè)試 } catch (Exception e) { e.printStackTrace(); } } /** * 循環(huán)打印時(shí)間 */ public static void printNumber() { executor.execute(new Runnable() { @Override public void run() { while (true) { try { printOnceASecond(); } catch (Exception e) { e.printStackTrace(); } } } }); } /** * 一秒打印一次時(shí)間 * * @throws Exception */ public static void printOnceASecond() throws Exception { System.out.println(getCurrentTime()); Thread.sleep(1000); } /** * 獲取當(dāng)前時(shí)間 * * @return */ public static String getCurrentTime() { return LocalDateTime.now().format(DateTimeFormatter.ofPattern(timeFormat)); } }
OK,接下來(lái)我們就來(lái)測(cè)試下鎖的互斥性以及使用注意事項(xiàng)(都是多線程的情況下)。
開(kāi)始測(cè)試同一個(gè)對(duì)象同步代碼塊
public static void main(String[] args) throws Exception { printNumber();//控制臺(tái)循環(huán)打印時(shí)間 Synchronized es = new Synchronized(); executor.execute(() -> es.synchronizedCode()); executor.execute(() -> es.synchronizedCode()); }
execute
20:34:41:I am synchronized Code 20:34:41 20:34:42 20:34:43 20:34:44 20:34:45 20:34:46:I am synchronized Code
同步代碼塊中休眠5秒,導(dǎo)致另外一個(gè)線程阻塞5秒后再執(zhí)行。說(shuō)明代同步碼塊會(huì)阻塞同一個(gè)對(duì)象的不同線程之間的調(diào)用(同步方法和同步靜態(tài)方法也會(huì)阻塞同一個(gè)對(duì)象的不同線程之間的調(diào)用,此處省略測(cè)試代碼)
不同對(duì)象同步代碼塊
public static void main(String[] args) throws Exception { printNumber();//控制臺(tái)循環(huán)打印時(shí)間 Synchronized es = new Synchronized(); Synchronized es1 = new Synchronized(); executor.execute(() -> es.synchronizedCode()); executor.execute(() -> es1.synchronizedCode()); }
execute
20:44:34:I am synchronized Code 20:44:34:I am synchronized Code
由結(jié)果可以看出,不同對(duì)象之間代碼塊鎖互不影響(多線程也一樣)。原因是因?yàn)榇a塊中synchronized (this)
鎖的是當(dāng)前調(diào)用對(duì)象,不同對(duì)象之間不是同一把鎖,因此互不影響(同步方法原理也是如此,省略測(cè)試代碼)。
同一對(duì)象同步代碼塊和方法
public static void main(String[] args) throws Exception { printNumber();//控制臺(tái)循環(huán)打印時(shí)間 Synchronized es = new Synchronized(); executor.execute(() -> es.synchronizedCode()); executor.execute(() -> es.synchronizedMethod()); }
execute
20:51:27:I am synchronized method 20:51:27 20:51:28 20:51:29 20:51:30 20:51:31 20:51:32:I am synchronized Code
因?yàn)橥酱a塊和同步方法,都是鎖當(dāng)前調(diào)用對(duì)象,因此執(zhí)行后打印上述結(jié)果應(yīng)該在意料之中?;谶@樣的特性,實(shí)際開(kāi)發(fā)在使用spring的時(shí)候就需要注意了,我們的bean交給spring容器管理之后,默認(rèn)都是單例的。那么這個(gè)時(shí)候使用synchronized關(guān)鍵字就需要注意了(推薦使用同步代碼塊,同步的代碼塊中傳入外部定義的一個(gè)變量)。
不同對(duì)象靜態(tài)同步方法
public static void main(String[] args) throws Exception { printNumber();//控制臺(tái)循環(huán)打印時(shí)間 Synchronized es = new Synchronized(); Synchronized es1 = new Synchronized(); executor.execute(() -> es.synchronizedStaticMethod()); executor.execute(() -> es1.synchronizedStaticMethod()); }
execute
21:05:39:I am synchronized static method 21:05:40 21:05:41 21:05:42 21:05:43 21:05:44:I am synchronized static method
由上述結(jié)果可以看出來(lái),靜態(tài)同步方法會(huì)阻塞所有的對(duì)象。原因是所有的靜態(tài)同步方法都是占用的同一把鎖。
相同對(duì)象同步方法和靜態(tài)同步方法
public static void main(String[] args) throws Exception { printNumber();//控制臺(tái)循環(huán)打印時(shí)間 Synchronized es = new Synchronized(); executor.execute(() -> es.synchronizedMethod()); executor.execute(() -> es.synchronizedStaticMethod()); }
execute
21:11:03:I am synchronized static method 21:11:03:I am synchronized method
由此結(jié)果可以看出,同步方法和靜態(tài)同步方法之間不會(huì)造成阻塞的現(xiàn)象。因?yàn)樗麄冩i的對(duì)象不一樣。同步方法占用的鎖是調(diào)用對(duì)象,靜態(tài)同步方法鎖的是編譯后的class對(duì)象(類鎖)。
總結(jié)同一個(gè)對(duì)象,同步方法、同步代碼塊之間互斥,同時(shí)和自己也互斥。靜態(tài)同步方法只和自己互斥
不同對(duì)象之間,同步方法、同步代碼塊不會(huì)互斥。靜態(tài)同步方法會(huì)互斥
synchronized在占用鎖時(shí),必須精確到某一個(gè)具體的對(duì)象
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74420.html
摘要:每個(gè)對(duì)象只有一個(gè)鎖與之相關(guān)聯(lián)。實(shí)現(xiàn)同步則是以系統(tǒng)開(kāi)銷作為代價(jià),甚至可能造成死鎖,所以盡量避免濫用。這種機(jī)制確保了同一時(shí)刻該類實(shí)例,所有聲明為的函數(shù)中只有一個(gè)方法處于可執(zhí)行狀態(tài),從而有效避免了類成員變量訪問(wèn)沖突。 synchronized是JAVA語(yǔ)言的一個(gè)關(guān)鍵字,使用 synchronized 來(lái)修飾方法或代碼塊的時(shí)候,能夠保證多個(gè)線程中最多只有一個(gè)線程執(zhí)行該段代碼 ... 概述 ...
摘要:用法中規(guī)定,在調(diào)用者三個(gè)方法時(shí),當(dāng)前線程必須獲得對(duì)象鎖。作用方法作用線程自動(dòng)釋放占有的對(duì)象鎖,并等待。當(dāng)生產(chǎn)者生產(chǎn)了一個(gè)數(shù)據(jù)或者消費(fèi)者消費(fèi)了一個(gè)數(shù)據(jù)之后,使用方法來(lái)通知所有等待當(dāng)前對(duì)象鎖的線程,但是一次只會(huì)有一個(gè)等待的線程能拿到鎖。 基礎(chǔ)知識(shí) 首先我們需要知道,這幾個(gè)都是Object對(duì)象的方法。換言之,Java中所有的對(duì)象都有這些方法。 public final native void...
摘要:同步代碼塊二類,鎖是小括號(hào)中的類對(duì)象對(duì)象。因?yàn)閷?duì)于同一個(gè)實(shí)例對(duì)象,各線程之間訪問(wèn)其中的同步方法是互斥的。優(yōu)化同步代碼塊的方式有,減少同步區(qū)域或減小鎖的范圍。 版權(quán)聲明:本文由吳仙杰創(chuàng)作整理,轉(zhuǎn)載請(qǐng)注明出處:https://segmentfault.com/a/1190000009225706 1. 引言 在 Java 多線程編程中,我們常需要考慮線程安全問(wèn)題,其中關(guān)鍵字 synchro...
摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對(duì)象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對(duì)象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
閱讀 663·2021-11-24 09:39
閱讀 3037·2021-11-23 10:06
閱讀 993·2021-10-08 10:05
閱讀 772·2019-08-30 10:49
閱讀 1741·2019-08-29 14:08
閱讀 1334·2019-08-29 12:48
閱讀 3330·2019-08-26 14:04
閱讀 3624·2019-08-26 13:50