Java并發篇_synchronized

synchronized是Java語言的關鍵字,當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多只有一個線程執行該段代碼。本文給大家介紹java中的用法。

一、為什么要使用synchronized

在并發編程中存在線程安全問題,主要原因有:

1.存在共享數據

2.多線程共同操作共享數據。

關鍵字synchronized可以保證在同一時刻,只有一個線程可以執行某個方法或某個代碼塊,同時synchronized可以保證一個線程的變化可見(可見性),即可以代替volatile。

synchronized可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共享變量的內存可見性

二、synchronized的用法

synchronized可以修飾靜態方法、成員函數,同時還可以直接定義代碼塊,但是歸根結底它上鎖的資源只有兩類:一個是對象,一個是

  1. 修飾普通方法(實例方法),鎖是當前實例對象 ,進入同步代碼前要獲得當前實例的鎖
  2. 修飾靜態方法,鎖是當前類的class對象 ,進入同步代碼前要獲得當前類對象的鎖
  3. 修飾方法塊,鎖是括號里面的對象,對給定對象加鎖,進入同步代碼庫前要獲得給定對象的鎖。

三、實現原理

我們先通過反編譯下面的代碼來看看Synchronized是如何實現對代碼塊進行同步的:

 package com.paddx.test.concurrent;public class SynchronizedDemo {public void method() {synchronized (this) {System.out.println("Method 1 start");}}}

反編譯結果:

img

關于這兩條指令的作用,我們直接參考JVM規范中描述:

monitorenter :

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
? If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
? If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
? If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership.

這段話的大概意思為:

每個對象有一個監視器鎖(monitor)。當monitor被占用時就會處于鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:

1、如果monitor的進入數為0,則該線程進入monitor,然后將進入數設置為1,該線程即為monitor的所有者。

2、如果線程已經占有該monitor,只是重新進入,則進入monitor的進入數加1.

3.如果其他線程已經占用了monitor,則該線程進入阻塞狀態,直到monitor的進入數為0,再重新嘗試獲取monitor的所有權。

monitorexit:

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

這段話的大概意思為:

執行monitorexit的線程必須是objectref所對應的monitor的所有者。

指令執行時,monitor的進入數減1,如果減1后進入數為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去獲取這個 monitor 的所有權。

通過這兩段描述,我們應該能很清楚的看出Synchronized的實現原理,Synchronized的語義底層是通過一個monitor的對象來完成,其實wait/notify等方法也依賴于monitor對象,這就是為什么只有在同步的塊或者方法中才能調用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。

我們再來看一下同步方法的反編譯結果:

源代碼:

1 package com.paddx.test.concurrent;
2 
3 public class SynchronizedMethod {
4     public synchronized void method() {
5         System.out.println("Hello World!");
6     }
7 }

反編譯結果:

img

從反編譯的結果來看,方法的同步并沒有通過指令monitorenter和monitorexit來完成(理論上其實也可以通過這兩條指令來實現),不過相對于普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。JVM就是根據該標示符來實現方法的同步的:當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標志是否被設置,如果設置了,執行線程將先獲取monitor,獲取成功之后才能執行方法體,方法執行完后再釋放monitor。在方法執行期間,其他任何線程都無法再獲得同一個monitor對象。 其實本質上沒有區別,只是方法的同步是一種隱式的方式來實現,無需通過字節碼來完成。

四、總結

Synchronized是Java并發編程中最常用的用于保證線程安全的方式,其使用相對也比較簡單。但是如果能夠深入了解其原理,對監視器鎖等底層知識有所了解,一方面可以幫助我們正確的使用Synchronized關鍵字,另一方面也能夠幫助我們更好的理解并發編程機制,有助我們在不同的情況下選擇更優的并發策略來完成任務。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/535659.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/535659.shtml
英文地址,請注明出處:http://en.pswp.cn/news/535659.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysqlreport的學習

mysqlreport是一個腳本. 需要先安裝perl-DBI和perl-DBD-MySQL這2個包 mysqlreport 使用DBI 需要有http://hackmysql.com/mysqlreportdocperl ./mysqlreport --help 看幫助 perl ./mysqlreport --user root --password 密碼mysqlreport 文檔mysqlreport 以很友好的方式顯示 My…

Java并發篇_volatile

volatile是Java提供的一種輕量級的同步機制。Java 語言包含兩種內在的同步機制:同步塊(或方法)和 volatile 變量,相比于synchronized(synchronized通常稱為重量級鎖),volatile更輕量級&#xff…

vi 語法著色

我所在部門的經理極其鄙視我用vi,這到不是說他看不慣vi,而是因為那句話"只有黑客級的人才用VI".而我只是一只小小萊鳥.所以只好被他們鄙視了. 現在說一說vi 著色的問題. 首先安裝 vim-enhanced , # yum -y install vim-enhanced 然后, # vi ~/…

Docker Dockerfile詳解

一、什么是Dockerfile Dockerfile是一個包含用于組合映像的命令的文本文檔。可以使用在命令行中調用任何命令。 Docker通過讀取Dockerfile中的指令自動生成映像。 docker build命令用于從Dockerfile構建映像。可以在docker build命令中使用-f標志指向文件系統中任何位置的Doc…

公司臺灣主站的url重寫

今天對公司臺灣主站的url地址進行優化.主站采用的是joomla,而joomla初建好后用的url對搜索引擎非常的不友好. Joomla中的SEF說白了就是一個對URL的重寫的過程將原來參數眾多,層次很深的URL改寫為一個簡單的更容易被記住被搜索的URL。通過分析Joomla站點的URL結果就…

編寫第一個Spring程序——IOC實現

第一個Spring程序 IOC范例 1、新建maven工程 2、在pom.xml文件中導入相關jar包 <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --><dependency><groupId>org.springframework</groupId><artifactId>spring-core&l…

改變centos系統的時區

兩條命令都可以: 1.timeconfig 2.tzselect

分布式文件系統:原理、問題與方法

本地文件系統如ext3&#xff0c;reiserfs等&#xff08;這里不討論基于內存的文件系統&#xff09;&#xff0c;它們管理本地的磁盤存儲資源、提供文件到存儲位置的映射&#xff0c;并抽象出一套文件訪問接口供用戶使用。但隨著互聯網企業的高速發展&#xff0c;這些企業對數據…

編寫第二個Spring程序——AOP實現

第二個Spring程序 AOP范例 1、新建maven工程 2、在pom.xml文件導入相關jar包 <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --><dependency><groupId>org.springframework</groupId><artifactId>spring-core<…

linux高負載下徹底優化mysql數據庫

同時在線訪問量繼續增大 對于1G內存的服務器明顯感覺到吃力嚴重時甚至每天都會死機 或者時不時的服務器卡一下 這個問題曾經困擾了我半個多月MySQL使用是很具伸縮性的算法&#xff0c;因此你通常能用很少的內存運行或給MySQL更多的被存以得到更好的性能。 安裝好mysql后&#x…

Java注釋說明以及IDEA中的快捷鍵

一、單行注釋 說明&#xff1a;單行注釋 一般注釋少量的代碼或者說明內容 格式&#xff1a;//注釋的內容 IDEA中的快捷鍵&#xff1a;使用Ctrl /&#xff0c; 添加行注釋&#xff0c;再次使用&#xff0c;去掉行注釋 二、多行注釋 說明&#xff1a;多行注釋 一般注釋大量的…

redhat系統雙網卡綁定

Redhat Linux的網絡配置&#xff0c;基本上是通過修改幾個配置文件來實現的&#xff0c;雖然也可以用ifconfig來設置IP&#xff0c;用route來配置默認網關&#xff0c;用hostname來配置主機名&#xff0c;但是重啟后會丟失。 1.相關的配置文件: /ect/hosts 配置主機名和IP地址…

JDK源碼解析之java.util.Iterator和java.lang.Iterable

在Java中&#xff0c;我們可以對List集合進行如下幾種方式的遍歷&#xff1a;第一種就是普通的for循環&#xff0c;第二種為迭代器遍歷&#xff0c;第三種是for each循環。后面兩種方式涉及到Java中的iterator和iterable對象&#xff0c;接下來我們通過源碼來看看這兩個對象的區…

為了讓你的網頁能在更多的服務器上正常地顯示,還是加上“SET NAMES UTF8”吧

Repinted:http://blog.csdn.net/class1/archive/2006/12/30/1469298.aspx 為了讓你的網頁能在更多的服務器上正常地顯示&#xff0c;還是加上“SET NAMES UTF8”吧(可以根據你的喜歡選擇相應的編碼,如gb2312)&#xff0c;即使你現在沒有加上這句也能正常訪問。 先說MySQL的字…

WebLogic11g 安裝配置規范

目錄 1 文檔控制... 3 1.1 修改記錄... 3 1.2 分發者... 3 1.3 審閱記錄... 3 1.4 相關文檔... 3 2 安裝準備... 4 2.1 安裝前需要開發單位提供的信息... 4 2.2 本地磁盤空間配置規范... 4 2.3 版本要求規范... 4 2.4 weblogic部署配置規范... 5 2.4.1操作系統要求.…

JDK源碼解析之java.util.ListIterator

ListIterator是一個功能更加強大的迭代器接口, 它繼承于Iterator接口,只能用于各種List類型的訪問。可以通過調用listIterator()方法產生一個指向List開始處的ListIterator, 還可以調用listIterator(n)方法創建一個一開始就指向列表索引為n的元素處的ListIterator。 一、源碼解…

VsFTP出現500 OOPS: cannot change directory的解決辦法

cannot change directory:/home/*** ftp服務器連接失敗,錯誤提示:500 OOPS: cannot change directory:/home/*******500 OOPS: child died解決方法:在終端輸入命令&#xff1a;setsebool ftpd_disable_trans 1 service vsftpd restart就&#xff2f;&#xff2b;了&#xff01;…

Oracle的reman命令

list命令&#xff1a; list backupset summary 列出概要信息 list backupset by file list archivelog all 列出所有歸檔日志 list backupset tag 00列出標簽信息 list backupset 8 列出8號…

Ubuntu root賬號的使用

第一次安裝好Ubuntu后&#xff0c;root帳號不能用。在安裝期間創建的第一個用戶對系統有管理權&#xff0c;通過“sudo”能象root運行程序.使用時僅需它的普通用戶密碼。例如: sudo apt-get update  如果你希望像傳統 UNIX 樣式使用root帳號。你能通過輸入 sudo passwd root …

JDK源碼解析之Java.util.Collection

Collection是單例集合的頂層接口&#xff0c;它表示一組對象&#xff0c;這些對象也稱為Collection的元素&#xff0c;JDK 不提供此接口的任何直接實現&#xff0c;它提供更具體的子接口&#xff08;如Set和List&#xff09;實現 一、源碼解析 1、接口定義 public interface …