多線程訪問一個共享資源時,可能會致使運行結果不是預計的結果。數據庫
好比兩個線程要往數據庫中插入一條數據。安全
兩個線程先檢查數據庫中有沒有這個數據,都檢測到沒有,因而都插入一條數據。但實際上只須要一條數據。多線程
對共享資源操做時,纔會有線程安全問題。線程
序列化訪問臨界資源。同一時刻,只有一個線程訪問臨界資源。也叫同步互斥訪問。code
synchronized和Lock對象
做用是給共享資源加上互斥鎖。當一個線程訪問時,其餘線程等待訪問。接口
synchronized能夠鎖方法,也能夠鎖代碼塊。資源
synchronized的本質是鎖對象。同步
Java中,每個對象都有一個所標記monitor。多線程訪問某個對象時,線程只有得到了該對象的鎖,才能訪問。源碼
1.一個線程訪問一個對象的synchronized方法時,另外一個線程不能訪問該對象的synchronized方法,可是能夠訪問非synchronized方法。
2.一個線程訪問一個對象object1的synchronized方法時,另外一個線程能夠訪問對象object2的synchronized方法。object1和object2是同一個類型。 由於訪問的對象不同。
3.一個線程訪問一個對象的非靜態synchronized方法時,另外一個線程能夠訪問該對象的靜態synchronized方法。
由於靜態synchronized方法,佔領的鎖是類鎖,不是對象鎖。
synchronized編譯成兩條指令:monitorenter和monitorexit
monitorenter會讓對象的鎖計數加1。 monitorexist會讓對象的鎖計數減1。
拋出異常時,會釋放鎖對象,不會出現死鎖。
synchronized釋放鎖有兩種狀況:1.線程執行完,2.出現中斷異常。
若是synchronized中線程阻塞,其餘線程就要一直等待,很是影響效率。
因而就提供了功能更強大的Lock,來解決一些問題
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
synchronized是Java的內置關鍵字。Lock是一個接口。
Lock中有這麼幾個方法:
lock()用來獲取鎖,若是其餘線程已經獲取了鎖,那麼就等待。
和synchronized不同。Lock不會自動釋放鎖。因此必須手動釋放鎖,用unLock()。 通常放在try...catch()...finally()中。
tryLock有返回值,嘗試獲取鎖,若是獲取了鎖,就返回true,沒有獲取鎖,就返回false。也就是說,這個方法不會等待鎖。
通常用if(tryLock())..else...
使用lockInterruptibly()獲取鎖的狀況下,若是線程處於等待獲取鎖的狀態,調用interrupt能夠中斷線程,拋出異常。
實現了Lock的類。能夠實現可重入鎖。
是一個接口。定義了兩個方法。ReentrantReadWriteLock實現了該接口。
readLock()和writeLock()分別獲取讀鎖和寫鎖。
一個線程獲取了讀鎖,另外一個線程能夠得到該對象的讀鎖,不能得到該對象的寫鎖。
一個線程獲取了寫鎖,另外一個線程不能夠獲取該對象的讀寫鎖。
支持降級,寫鎖能夠降級爲讀鎖,可是讀鎖不能升級爲寫鎖。
1.synchronized是Java內置關鍵字,Lock是接口。
2.synchronized異常會自動釋放鎖,Lock要手動釋放。
3.synchronized沒法中斷等待獲取鎖的線程,Lock能夠。
4.synchronized沒法判斷線程是否獲取鎖,Lock能夠。
5.Lock可使用讀鎖,提升讀線程的效率。
一個鎖方法中調用另外一個鎖方法,線程不須要從新獲取鎖對象。
synchronized和Lock都是可重入鎖
等待獲取鎖的線程按請求順序獲取鎖對象。
synchronized是非公平鎖。 Lock默認是非公平鎖。可是能夠設置爲公平鎖。
ReentrantLock lock = new ReentrantLock(true);
能夠中斷等待鎖的線程。
synchronized是不可中斷鎖。Lock是可中斷鎖。
鎖分紅兩個,一個讀鎖,一個寫鎖。
synchronized沒有,ReadWriteLock是讀寫鎖。