本篇主要介紹Java多線程中的同步,也就是如何在Java語言中寫出線程安全的程序,如何在Java語言中解決非線程安全的相關問題。沒錯就是使用synchronized。
如何解決線程安全問題?那麼一般來說,是如何解決線程安全問題的呢?
基本上所有的併發模式在解決線程安全問題時,都採用「序列化訪問臨界資源」的方案,即在同一時刻,只能有一個線程訪問臨界資源,也稱作同步互斥訪問。
通常來說,是在訪問臨界資源的代碼前面加上一個鎖,當訪問完臨界資源後釋放鎖,讓其他線程繼續訪問。
在Java中,提供了兩種方式來實現同步互斥訪問:synchronized和Lock。
本文主要講述synchronized的使用方法,Lock的使用方法在下一篇博文中講述。
synchronized同步方法synchronized是Java語言的關鍵字,當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多只有一個線程執行該段代碼。在了解synchronized關鍵字的使用方法之前,我們先來看一個概念:互斥鎖,顧名思義:能達到互斥訪問目的的鎖。
舉個簡單的例子:如果對臨界資源加上互斥鎖,當一個線程在訪問該臨界資源時,其他線程便只能等待。
在Java中,每一個對象都擁有一個鎖標記(monitor),也稱為監視器,多線程同時訪問某個對象時,線程只有獲取了該對象的鎖才能訪問。
在Java中,可以使用synchronized關鍵字來標記一個方法或者代碼塊,當某個線程調用該對象的synchronized方法或者訪問synchronized代碼塊時,這個線程便獲得了該對象的鎖,其他線程暫時無法訪問這個方法,只有等待這個方法執行完畢或者代碼塊執行完畢,這個線程才會釋放該對象的鎖,其他線程才能執行這個方法或者代碼塊。
synchronized的使用synchronized代碼塊,被修飾的代碼成為同步語句塊,其作用的範圍是調用這個代碼塊的對象,我們在用synchronized關鍵字的時候,能縮小代碼段的範圍就儘量縮小,能在代碼段上加同步就不要再整個方法上加同步。這叫減小鎖的粒度,使代碼更大程度的並發。
synchronized方法,被修飾的方法成為同步方法,其作用範圍是整個方法,作用對象是調用這個方法的對象。
synchronized靜態方法,修飾一個static靜態方法,其作用範圍是整個靜態方法,作用對象是這個類的所有對象。
synchronized類,其作用範圍是Synchronized後面括號括起來的部分synchronized(className.class),作用的對象是這個類的所有對象。
synchronized(),()中是鎖住的對象, synchronized(this)鎖住的只是對象本身,同一個類的不同對象調用的synchronized方法並不會被鎖住,而synchronized(className.class)實現了全局鎖的功能,所有這個類的對象調用這個方法都受到鎖的影響,此外()中還可以添加一個具體的對象,實現給具體對象加鎖。
synchronized (object) {}
synchronized注意事項當兩個並發線程訪問同一個對象中的synchronized代碼塊時,在同一時刻只能有一個線程得到執行,另一個線程受阻塞,必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。兩個線程間是互斥的,因為在執行synchronized代碼塊時會鎖定當前的對象,只有執行完該代碼塊才能釋放該對象鎖,下一個線程才能執行並鎖定該對象。
當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。(兩個線程使用的是同一個對象)
當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞(同上,兩個線程使用的是同一個對象)。
下面通過代碼來實現:
1)當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。
2)然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
3)尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
4)第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
5)每個類也會有一個鎖,它可以用來控制對static數據成員的並發訪問。
並且如果一個線程執行一個對象的非static synchronized方法,另外一個線程需要執行這個對象所屬類的static synchronized方法,此時不會發生互斥現象,因為訪問static synchronized方法佔用的是類鎖,而訪問非static synchronized方法佔用的是對象鎖,所以不存在互斥現象。
代碼如下:
第一個線程裡面執行的是insert方法,不會導致第二個線程執行insert1方法發生阻塞現象。
面試題當一個線程進入一個對象的synchronized方法A之後,其它線程是否可進入此對象的synchronized方法B?
答:不能。其它線程只能訪問該對象的非同步方法,同步方法則不能進入。因為非靜態方法上的synchronized修飾符要求執行方法時要獲得對象的鎖,如果已經進入A方法說明對象鎖已經被取走,那麼試圖進入B方法的線程就只能在等鎖池(注意不是等待池哦)中等待對象的鎖。
synchronized關鍵字的用法?
答:synchronized關鍵字可以將對象或者方法標記為同步,以實現對對象和方法的互斥訪問,可以用synchronized(對象) { … }定義同步代碼塊,或者在聲明方法時將synchronized作為方法的修飾符。
簡述synchronized 和java.util.concurrent.locks.Lock的異同?
答:Lock是Java 5以後引入的新的API,和關鍵字synchronized相比主要相同點:Lock 能完成synchronized所實現的所有功能;主要不同點:Lock有比synchronized更精確的線程語義和更好的性能,而且不強制性的要求一定要獲得鎖。synchronized會自動釋放鎖,而Lock一定要求程式設計師手工釋放,並且最好在finally 塊中釋放(這是釋放外部資源的最好的地方)
原文作者:嘟嘟MD
原文地址:http://www.jianshu.com/p/9474d64575b0
來源:網絡。若涉及版權問題,煩請原作者聯繫我們,我們會在24小時內刪除處理,謝謝!
中軟高科
微信號:javaedu
(長按上圖,彈出「識別二維碼」後可快速關注)