1.排他鎖(互斥鎖)的概念:java
synchronized,ReentrantLock這些鎖都是排他鎖,這些鎖同一時刻只容許一個線程進行訪問。緩存
2.讀寫鎖的概念:dom
分爲讀鎖和寫鎖,多個讀鎖不互斥,讀鎖和寫鎖互斥,寫鎖與寫鎖互斥。性能
3.讀寫鎖的好處:this
爲了提升性能,Java提供了讀寫鎖,在讀的地方使用讀鎖,在寫的地方使用寫鎖,靈活控制,若是沒有寫鎖的狀況下,讀是無阻塞的,在必定程度上提升了程序的執行效率。.net
原來使用的互斥鎖只能同時間有一個線程在運行,如今的讀寫鎖同一時刻能夠多個讀鎖同時運行,這樣的效率比原來的排他鎖(互斥鎖)效率高。線程
4.讀寫鎖的原理分析:code
Java中讀寫鎖有個接口java.util.concurrent.locks.ReadWriteLock,也有具體的實現ReentrantReadWriteLock,接口
lock方法 是基於CAS 來實現的進程
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading. */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing. */ Lock writeLock(); }
5.案例一:
package WriteReadLock; import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriterLockTest { public static void main(String[] args) { final Queue q3 = new Queue(); for(int i=0;i<3;i++) { new Thread(){ public void run(){ while(true){ q3.get(); } } }.start(); } for(int i=0;i<3;i++) { new Thread(){ public void run(){ while(true){ q3.put(new Random().nextInt(10000)); } } }.start(); } } } class Queue{ //共享數據,只能有一個線程能寫該數據,但能夠有多個線程同時讀該數據。 private Object data = null; //獲得讀寫鎖 ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** * 將用於讀的get()和寫的put()放在同一個類中這樣是爲了對同一個資源data進行操做,造成互斥 */ /** * 進行讀操做 * 能夠多個讀線程同時進入,寫線程不能執行 */ public void get(){ //獲取讀鎖,並加鎖 Lock readLock = readWriteLock.readLock(); readLock.lock(); try { System.out.println(Thread.currentThread().getName() + " be ready to read data!"); Thread.sleep((long)(Math.random()*1000)); System.out.println(Thread.currentThread().getName() + "have read data :"+ data); } catch (InterruptedException e) { e.printStackTrace(); }finally { //!!!!!!注意:鎖的釋放必定要在trycatch的finally中,由於若是前面程序出現異常,鎖就不能釋放了 //釋放讀鎖 readLock.unlock(); } } /** * 進行寫操做 * 只能一個寫線程進入,讀線程不能執行 */ public void put(Object data){ //獲取寫鎖,並加鎖 Lock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { System.out.println(Thread.currentThread().getName() + " be ready to write data!"); Thread.sleep((long)(Math.random()*1000)); this.data = data; System.out.println(Thread.currentThread().getName() + " have write data: " + data); } catch (InterruptedException e) { e.printStackTrace(); }finally { //釋放寫鎖 writeLock.unlock(); } } }
沒有使用鎖以前:讀和寫交叉在一塊兒
在加入讀寫鎖以後:讀的過程當中,不會有寫
6.案例二:
package WriteReadLock; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class CacheDemo { //用map來模擬緩存 Map<String,Object> cache = new HashMap<String,Object>(); ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public static void main(String[] args) { final CacheDemo cacheDemo = new CacheDemo(); for(int i=0;i<6;i++) { new Thread(){ public void run(){ while(true){ System.out.println( cacheDemo.getData("key1").toString()); } } }.start(); } } Lock readLock = readWriteLock.readLock(); Lock writeLock = readWriteLock.writeLock(); //這裏必需要用volatie當一個寫線程設置value="aaaabbbb",必定要讓其餘的線程知道vlue的變化,這樣就不會被重複寫 volatile Object value; public Object getData(String key){ readLock.lock(); try { Thread.sleep(300); System.out.println(" read"); value = cache.get(key); if (value == null) { //這裏已經加了讀鎖,讀鎖中寫是不能容許的,因此要把這個鎖釋放掉 readLock.unlock(); writeLock.lock(); //防止,當多個寫者進程在等待,前面的寫進程已經賦值了,value已經不爲空了後面的等着的寫進程仍然繼續賦值 if(value == null){ System.out.println("find null"); value="aaaabbbb"; cache.put(key, value); System.out.println("write"); } writeLock.unlock(); //重新加上讀鎖 readLock.lock(); } return value; } catch (Exception e) { e.printStackTrace(); }finally { readLock.unlock(); } return null; } }