# [synchronized 與 Lock 的那點事](http://www.cnblogs.com/benshan/p/3551987.html)
[TOC=1,2]
最近在做一個監控系統,該系統主要包括對數據實時分析和存儲兩個部分,由于并發量比較高,所以不可避免的使用到了一些并發的知識。為了實現這些要求,后臺使用一個隊列作為緩存,對于請求只管往緩存里寫數據。同時啟動一個線程監聽該隊列,檢測到數據,立即請求調度線程,對數據進行處理。 具體的使用方案就是使用同步保證數據的正常,使用線程池提高效率。
同步的實現當然是采用鎖了,java中使用鎖的兩個基本工具是 synchronized 和 Lock。
一直很喜歡synchronized,因為使用它很方便。比如,需要對一個方法進行同步,那么只需在方法的簽名添加一個synchronized關鍵字。
// 未同步的方法
public void test() {}
// 同步的方法
pubilc synchronized void test() {}
synchronized 也可以用在一個代碼塊上,看
public void test() {
? ? ?synchronized(obj) {
? ? ? ? ? System.out.println("===");
?????}
}
synchronized 用在方法和代碼塊上有什么區別呢?
synchronized 用在方法簽名上(以test為例),當某個線程調用此方法時,會獲取該實例的對象鎖,方法未結束之前,其他線程只能去等待。當這個方法執行完時,才會釋放對象鎖。其他線程才有機會去搶占這把鎖,去執行方法test,但是發生這一切的基礎應當是所有線程使用的同一個對象實例,才能實現互斥的現象。否則synchronized關鍵字將失去意義。
(**但是如果該方法為類方法,即其修飾符為static,那么synchronized 意味著某個調用此方法的線程當前會擁有該類的鎖,只要該線程持續在當前方法內運行,其他線程依然無法獲得方法的使用權!**)
synchronized 用在代碼塊的使用方式:synchronized(obj){//todo code here}
當線程運行到該代碼塊內,就會擁有obj對象的對象鎖,如果多個線程共享同一個Object對象,那么此時就會形成互斥!特別的,當obj == this時,表示當前調用該方法的實例對象。即
public void test() {
?????...
?????synchronized(this) {
? ? ??????// todo your code
?????}
?????...
}
此時,其效果等同于
public synchronized void test() {
? ? ?// todo?your code
}
使用synchronized代碼塊,可以只對需要同步的代碼進行同步,這樣可以大大的提高效率。
小結:
使用synchronized 代碼塊相比方法有兩點優勢:
1、可以只對需要同步的使用
2、與wait()/notify()/nitifyAll()一起使用時,比較方便
----------------------------------------------------------------------------------------------------------------------------------------------------------
wait() 與notify()/notifyAll()
**這三個方法都是Object的方法,并不是線程的方法!**
wait():釋放占有的對象鎖,線程進入等待池,釋放cpu,而其他正在等待的線程即可搶占此鎖,獲得鎖的線程即可運行程序。而sleep()不同的是,線程調用此方法后,會休眠一段時間,休眠期間,會暫時釋放cpu,但并不釋放對象鎖。也就是說,在休眠期間,其他線程依然無法進入此代碼內部。休眠結束,線程重新獲得cpu,執行代碼。**wait()和sleep()最大的不同在于wait()會釋放對象鎖,而sleep()不會!**
notify(): 該方法會喚醒因為調用對象的wait()而等待的線程,其實就是**對對象鎖的喚醒,從而使得wait()的線程可以有機會獲取對象鎖**。調用notify()后,并不會立即釋放鎖,而是繼續執行當前代碼,直到synchronized中的代碼全部執行完畢,才會釋放對象鎖。JVM則會在等待的線程中調度一個線程去獲得對象鎖,執行代碼。需要注意的是,**wait()和notify()必須在synchronized代碼塊中調用**。
notifyAll()則是喚醒所有等待的線程。
為了說明這一點,舉例如下:
兩個線程依次打印"A""B",總共打印10次。
**public**?**class**?Consumer?**implements**?Runnable {
?
?????@Override
?????**public**?**synchronized**?**void**?run() {
????????????//?**TODO**?Auto-generated method stub
????????????**int**?count = 10;
????????????**while**(count > 0) {
?????????????????**synchronized**?(Test.?*obj*) {
?????????????????????
?????????????????????System.?*out*.print(?"B");
?????????????????????count --;
?????????????????????Test.?*obj*.notify();?// 主動釋放對象鎖
?????????????????????
??????????????????????**try**?{
???????????????????????????Test.?*obj*.wait();
???????????????????????????
?????????????????????}?**catch**?(InterruptedException e) {
????????????????????????????//?**TODO**?Auto-generated catch block
???????????????????????????e.printStackTrace();
?????????????????????}
????????????????}
????????????????
???????????}
?????}
}
**public**?**class**?Produce?**implements**?Runnable {
?
?????@Override
?????**public**?**void**?run() {
????????????//?**TODO**?Auto-generated method stub
????????????**int**?count = 10;
????????????**while**(count > 0) {
?????????????????**synchronized**?(Test.?*obj*) {
?????????????????????
??????????????????????//System.out.print("count = " + count);
?????????????????????System.?*out*.print(?"A");
?????????????????????count --;
?????????????????????Test.?*obj*.notify();
?????????????????????
??????????????????????**try**?{
???????????????????????????Test.?*obj*.wait();
?????????????????????}?**catch**?(InterruptedException e) {
????????????????????????????//?**TODO**?Auto-generated catch block
???????????????????????????e.printStackTrace();
?????????????????????}
????????????????}
????????????????
???????????}
?
?????}
?
}
?
測試類如下:
**public**?**class**?Test {
?
?????**public**?**static**?**final**?Object?*obj*?=?**new**?Object();
?????
?????**public**?**static**?**void**?main(String[] args) {
???????????
????????????**new**?Thread(?**new**?Produce()).start();
????????????**new**?Thread(?**new**?Consumer()).start();
???????????
?????}
}
?
這里使用static obj作為鎖的對象,當線程Produce啟動時(假如Produce首先獲得鎖,則Consumer會等待),打印“A”后,會先主動釋放鎖,然后阻塞自己。Consumer獲得對象鎖,打印“B”,然后釋放鎖,阻塞自己,那么Produce又會獲得鎖,然后...一直循環下去,直到count = 0.這樣,使用Synchronized和wait()以及notify()就可以達到線程同步的目的。
----------------------------------------------------------------------------------------------------------------------------------------------------------
**除了wait()和notify()協作完成線程同步之外,使用Lock也可以完成同樣的目的。**
ReentrantLock 與synchronized有相同的并發性和內存語義,還包含了中斷鎖等候和定時鎖等候,意味著線程A如果先獲得了對象obj的鎖,那么線程B可以在等待指定時間內依然無法獲取鎖,那么就會自動放棄該鎖。
但是由于synchronized是在JVM層面實現的,因此系統可以監控鎖的釋放與否,而ReentrantLock使用代碼實現的,系統無法自動釋放鎖,需要在代碼中finally子句中顯式釋放鎖lock.unlock();
同樣的例子,使用lock 如何實現呢?
**public**?**class**?Consumer?**implements**?Runnable {
?
?????**private**?Lock?lock;
?????**public**?Consumer(Lock lock) {
????????????**this**.?lock?= lock;
?????}
?????@Override
?????**public**?**void**?run() {
????????????//?**TODO**?Auto-generated method stub
????????????**int**?count = 10;
????????????**while**( count > 0 ) {
?????????????????**try**?{
??????????????????????lock.lock();
?????????????????????count --;
?????????????????????System.?*out*.print(?"B");
????????????????}?**finally**?{
??????????????????????lock.unlock(); //主動釋放鎖
??????????????????????**try**?{
???????????????????????????Thread.?*sleep*(91L);
?????????????????????}?**catch**?(InterruptedException e) {
????????????????????????????//?**TODO**?Auto-generated catch block
???????????????????????????e.printStackTrace();
?????????????????????}
????????????????}
???????????}
?
?????}
?
}
**public**?**class**?Producer?**implements**?Runnable{
?
?????**private**?Lock?lock;
?????**public**?Producer(Lock lock) {
????????????**this**.?lock?= lock;
?????}
?????@Override
?????**public**?**void**?run() {
????????????//?**TODO**?Auto-generated method stub
????????????**int**?count = 10;
????????????**while**?(count > 0) {
?????????????????**try**?{
??????????????????????lock.lock();
?????????????????????count --;
?????????????????????System.?*out*.print(?"A");
????????????????}?**finally**?{
??????????????????????lock.unlock();
??????????????????????**try**?{
???????????????????????????Thread.?*sleep*(90L);
?????????????????????}?**catch**?(InterruptedException e) {
????????????????????????????//?**TODO**?Auto-generated catch block
???????????????????????????e.printStackTrace();
?????????????????????}
????????????????}
???????????}
?????}
}
?
調用代碼:
**public**?**class**?Test {
?
?????**public**?**static**?**void**?main(String[] args) {
???????????Lock lock =?**new**?ReentrantLock();
???????????
???????????Consumer consumer =?**new**?Consumer(lock);
???????????Producer?producer?=?**new**?Producer(lock);
???????????
????????????**new**?Thread(consumer).start();
????????????**new**?Thread(?producer).start();
???????????
?????}
}
使用建議:
在并發量比較小的情況下,使用synchronized是個不錯的選擇,但是在并發量比較高的情況下,其性能下降很嚴重,此時ReentrantLock是個不錯的方案。
--------------------------------------------------------------------------------------------------------------------------------------
- JVM
- 深入理解Java內存模型
- 深入理解Java內存模型(一)——基礎
- 深入理解Java內存模型(二)——重排序
- 深入理解Java內存模型(三)——順序一致性
- 深入理解Java內存模型(四)——volatile
- 深入理解Java內存模型(五)——鎖
- 深入理解Java內存模型(六)——final
- 深入理解Java內存模型(七)——總結
- Java內存模型
- Java內存模型2
- 堆內內存還是堆外內存?
- JVM內存配置詳解
- Java內存分配全面淺析
- 深入Java核心 Java內存分配原理精講
- jvm常量池
- JVM調優總結
- JVM調優總結(一)-- 一些概念
- JVM調優總結(二)-一些概念
- VM調優總結(三)-基本垃圾回收算法
- JVM調優總結(四)-垃圾回收面臨的問題
- JVM調優總結(五)-分代垃圾回收詳述1
- JVM調優總結(六)-分代垃圾回收詳述2
- JVM調優總結(七)-典型配置舉例1
- JVM調優總結(八)-典型配置舉例2
- JVM調優總結(九)-新一代的垃圾回收算法
- JVM調優總結(十)-調優方法
- 基礎
- Java 征途:行者的地圖
- Java程序員應該知道的10個面向對象理論
- Java泛型總結
- 序列化與反序列化
- 通過反編譯深入理解Java String及intern
- android 加固防止反編譯-重新打包
- volatile
- 正確使用 Volatile 變量
- 異常
- 深入理解java異常處理機制
- Java異常處理的10個最佳實踐
- Java異常處理手冊和最佳實踐
- Java提高篇——對象克隆(復制)
- Java中如何克隆集合——ArrayList和HashSet深拷貝
- Java中hashCode的作用
- Java提高篇之hashCode
- 常見正則表達式
- 類
- 理解java類加載器以及ClassLoader類
- 深入探討 Java 類加載器
- 類加載器的工作原理
- java反射
- 集合
- HashMap的工作原理
- ConcurrentHashMap之實現細節
- java.util.concurrent 之ConcurrentHashMap 源碼分析
- HashMap的實現原理和底層數據結構
- 線程
- 關于Java并發編程的總結和思考
- 40個Java多線程問題總結
- Java中的多線程你只要看這一篇就夠了
- Java多線程干貨系列(1):Java多線程基礎
- Java非阻塞算法簡介
- Java并發的四種風味:Thread、Executor、ForkJoin和Actor
- Java中不同的并發實現的性能比較
- JAVA CAS原理深度分析
- 多個線程之間共享數據的方式
- Java并發編程
- Java并發編程(1):可重入內置鎖
- Java并發編程(2):線程中斷(含代碼)
- Java并發編程(3):線程掛起、恢復與終止的正確方法(含代碼)
- Java并發編程(4):守護線程與線程阻塞的四種情況
- Java并發編程(5):volatile變量修飾符—意料之外的問題(含代碼)
- Java并發編程(6):Runnable和Thread實現多線程的區別(含代碼)
- Java并發編程(7):使用synchronized獲取互斥鎖的幾點說明
- Java并發編程(8):多線程環境中安全使用集合API(含代碼)
- Java并發編程(9):死鎖(含代碼)
- Java并發編程(10):使用wait/notify/notifyAll實現線程間通信的幾點重要說明
- java并發編程-II
- Java多線程基礎:進程和線程之由來
- Java并發編程:如何創建線程?
- Java并發編程:Thread類的使用
- Java并發編程:synchronized
- Java并發編程:Lock
- Java并發編程:volatile關鍵字解析
- Java并發編程:深入剖析ThreadLocal
- Java并發編程:CountDownLatch、CyclicBarrier和Semaphore
- Java并發編程:線程間協作的兩種方式:wait、notify、notifyAll和Condition
- Synchronized與Lock
- JVM底層又是如何實現synchronized的
- Java synchronized詳解
- synchronized 與 Lock 的那點事
- 深入研究 Java Synchronize 和 Lock 的區別與用法
- JAVA編程中的鎖機制詳解
- Java中的鎖
- TreadLocal
- 深入JDK源碼之ThreadLocal類
- 聊一聊ThreadLocal
- ThreadLocal
- ThreadLocal的內存泄露
- 多線程設計模式
- Java多線程編程中Future模式的詳解
- 原子操作(CAS)
- [譯]Java中Wait、Sleep和Yield方法的區別
- 線程池
- 如何合理地估算線程池大小?
- JAVA線程池中隊列與池大小的關系
- Java四種線程池的使用
- 深入理解Java之線程池
- java并發編程III
- Java 8并發工具包漫游指南
- 聊聊并發
- 聊聊并發(一)——深入分析Volatile的實現原理
- 聊聊并發(二)——Java SE1.6中的Synchronized
- 文件
- 網絡
- index
- 內存文章索引
- 基礎文章索引
- 線程文章索引
- 網絡文章索引
- IOC
- 設計模式文章索引
- 面試
- Java常量池詳解之一道比較蛋疼的面試題
- 近5年133個Java面試問題列表
- Java工程師成神之路
- Java字符串問題Top10
- 設計模式
- Java:單例模式的七種寫法
- Java 利用枚舉實現單例模式
- 常用jar
- HttpClient和HtmlUnit的比較總結
- IO
- NIO
- NIO入門
- 注解
- Java Annotation認知(包括框架圖、詳細介紹、示例說明)