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