1、先讀這篇文章,瞭解synchronized:Java線程同步:synchronized鎖住的是代碼仍是對象java
2、synchronized加鎖形成的問題
對象的方法中一旦加入synchronized修飾,則這個對象任什麼時候刻只能有一個線程訪問synchronized修飾的方法。假設有個數據對象擁有寫方法與讀方法,多線程環境中要想保證數據的安全,需對該對象的讀寫方法都要加入 synchronized同步塊。這樣任何線程在寫入時,其它線程沒法讀取與改變數據;若是有線程在讀取時,其餘線程也沒法讀取或寫入。這種方式在寫入操做遠大於讀操做時,問題不大,而當讀取遠遠大於寫入時,會形成性能瓶頸,由於此種狀況下讀取操做是能夠同時進行的,而加鎖操做限制了數據的併發讀取。這就引出了ReadWriteLock,讀寫鎖:分爲讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm本身控制的,你只要上好相應的鎖便可。若是你的代碼只讀數據,能夠不少人同時讀,但不能同時寫,那就上讀鎖;若是你的代碼修改數據,只能有一我的在寫,且不能同時讀取,那就上寫鎖。總之,讀的時候上讀鎖,寫的時候上寫鎖!
可是ReadWriteLock是個接口,ReentrantReadWriteLock是他的實現類,ReentrantReadWriteLock特性以下:安全
參考:ReentrantReadWriteLock讀寫鎖的使用
3、ReentrantReadWriteLock實例
ReentrantReadWriteLock實現了ReadWriteLock,使用ReentrantReadWriteLock,只須要在對象O(或者某個數據結構)中定義ReentrantReadWriteLock對象便可,而後調用lock或者unlock,便可對對象O實現加鎖操做。數據結構
package com.sf.log_gen; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class LockTest { public static void main(String[] args) { Count count = new Count(); new Thread(new Write(count)).start(); // 由於是不一樣的對象,因此這裏立馬就能得到ReadLock System.out.println(new Count().get()); // 針對同一對象to,由於子線程先得到了對象to的WriteLock(也有可能主線程先得到,主線程最好sleep(100L)), // 因此這裏ReadLock就須要等WriteLock釋放才能得到 System.out.println(count.get()); } } class Write implements Runnable { Count count = null; public Write(Count count) { this.count = count; } public void run() {// 在這個線程中先得到了writeLock this.count.lock(); this.count.add(); this.count.unlock(); } } class Count { private ReadWriteLock rwl = new ReentrantReadWriteLock(); int num = 0; public void add() { try { Thread.sleep(4000L); } catch (InterruptedException e) { e.printStackTrace(); } num++; } public int get() {// get方法若是不加鎖,則全部線程能夠直接讀取 this.rwl.readLock().lock(); int ret = num; this.rwl.readLock().unlock(); return ret; } public void lock() { this.rwl.writeLock().lock(); } public void unlock() { this.rwl.writeLock().unlock(); } }
4、悲觀鎖樂觀鎖 上面的鎖都是悲觀鎖
樂觀鎖即我先對num操做,操做完了以後,在判斷num是否有變化,沒變化,則說明沒有別的線程在對num操做,那我就能夠把我操做後的值賦值給num了。多線程