它提供3個經常使用的鎖併發
lock() : 獲不到鎖就就一直阻塞
trylock() :獲不到鎖就馬上放回 或者 定時的,輪詢的獲取鎖
lockInterruptibly() : 獲不到鎖時阻塞,但可接受中斷信號後退出阻塞狀態
基於衝突的樂觀併發策略:dom
若是共享數據被爭用,產生了衝突,那就再進行其餘的補償措施,好比說定時的獲取鎖,直到成功;不須要把線程掛起,也稱爲非阻塞的同步
公平: 多個線程在等待同一個鎖時,必須按照申請鎖的時間順序排隊等待
非公平: 在鎖釋放時,任何一個等待鎖的線程都有機會得到鎖,ReentrantLock構造方法,默然是非公平的ide
當你須要可定時的和可中斷的鎖操做,公平隊列,或者非塊結構的鎖,不然請使用synchronizedthis
public class MyReentrantLock { private ReentrantLock lock = new ReentrantLock(); public void write() { lock.lock(); try { long startTime = System.currentTimeMillis(); System.out.println("開始往這個buff寫入數據…"); for (;;)// 模擬要處理很長時間 { if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) { break; } } System.out.println("終於寫完了"); } finally { lock.unlock(); } } public void read() throws InterruptedException { lock.lockInterruptibly();// 注意這裏,能夠響應中斷 try { System.out.println("從這個buff讀數據"); } finally { lock.unlock(); } } public static void main(String args[]) { MyReentrantLock buff = new MyReentrantLock(); final Writer2 writer = new Writer2(buff); final Reader2 reader = new Reader2(buff); writer.start(); reader.start(); new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); for (;;) { if (System.currentTimeMillis() - start > 5000) { System.out.println("不等了,嘗試中斷"); reader.interrupt(); //此處中斷讀操做 break; } } } }).start(); } } class Reader2 extends Thread { private MyReentrantLock buff; public Reader2(MyReentrantLock buff) { this.buff = buff; } @Override public void run() { try { buff.read();//能夠收到中斷的異常,從而有效退出 } catch (InterruptedException e) { System.out.println("我不讀了"); } System.out.println("讀結束"); } } class Writer2 extends Thread { private MyReentrantLock buff; public Writer2(MyReentrantLock buff) { this.buff = buff; } @Override public void run() { buff.write(); } }
控制檯輸出:spa
開始往這個buff寫入數據…
不等了,嘗試中斷
我不讀了
讀結束
互斥:它使得讀寫操做互斥,讀讀操做不互斥
鎖降級:寫線程獲取寫入鎖後能夠獲取讀取鎖,而後釋放寫入鎖,這樣就從寫入鎖變成了讀取鎖
public class MyReadWriteLock { public static void main(String[] args) { PricesInfo pricesInfo = new PricesInfo(); Writer writer=new Writer(pricesInfo); Reader read = new Reader(pricesInfo); //寫線程 Thread tw=new Thread(writer); tw.start(); //多個讀線程 for (int i=0; i<5; i++){ Thread tr=new Thread(read); tr.start(); } } } //讀線程 class Reader implements Runnable{ private PricesInfo pricesInfo; public Reader(PricesInfo pricesInfo){ this.pricesInfo = pricesInfo; } @Override public void run() { pricesInfo.getPrice(); } } //寫線程 class Writer implements Runnable{ private PricesInfo pricesInfo; public Writer(PricesInfo pricesInfo){ this.pricesInfo = pricesInfo; } @Override public void run() { pricesInfo.setPrice(Math.random()*10); } } //數據實體 class PricesInfo { private double price; private ReadWriteLock lock = new ReentrantReadWriteLock(); public PricesInfo(){ } //讀鎖 public void getPrice(){ lock.readLock().lock(); System.out.println(Thread.currentThread().getName()+ " : in read*****************************"); System.out.println(Thread.currentThread().getName()+ ": 讀取數據= " + price); lock.readLock().unlock(); } //寫鎖 public void setPrice(double price){ lock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+ " :in Writer=============================================="); Thread.sleep(1000); this.price = price; System.out.println(Thread.currentThread().getName()+ ":寫入數據= " + price); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.writeLock().unlock(); } } }
控制檯輸出:線程
Thread-0 :in Writer============================================== Thread-0:寫入數據= 3.5843085966236266 Thread-3 : in read***************************** Thread-3: 讀取數據= 3.5843085966236266 ......
經過ReentrantLock的newCondition()獲得Condition對象,它用await()替換wait(),用signal()替換 notify(),用signalAll()替換notifyAll(), 實現線程間的通訊;code
若是是公平鎖,與Condition關聯的任務,以FIFO的形式獲取鎖,不然的話,是隨機獲取鎖;對象
消費者和生產者的例子blog
public class MyCondition{ public static void main(String args[]){ Info info = new Info(); //啓動生產者 Producer pro = new Producer(info) ; new Thread(pro).start() ; try{ Thread.sleep(100) ; }catch(InterruptedException e){ e.printStackTrace() ; } //啓動消費者 Consumer con = new Consumer(info) ; new Thread(con).start() ; } } class Info{ // 定義信息類 private String name = null; private String content = null ; private boolean flag = true ; // true生產, false消費 private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); //產生一個Condition對象 public void set(String name,String content){ lock.lock(); try{ while(!flag){ condition.await() ; } this.setName(name) ; Thread.sleep(300) ; this.setContent(content) ; flag = false ; // 改變標誌位,表示能夠取走 System.out.println("生產者: " + this.getName() + " --> " + this.getContent()) ; condition.signal(); }catch(InterruptedException e){ e.printStackTrace() ; }finally{ lock.unlock(); } } public void get(){ lock.lock(); try{ while(flag){ condition.await() ; } Thread.sleep(300) ; System.out.println("消費者: " + this.getName() + " --> " + this.getContent()) ; flag = true ; // 改變標誌位,表示能夠生產 condition.signal(); }catch(InterruptedException e){ e.printStackTrace() ; }finally{ lock.unlock(); } } public void setName(String name){ this.name = name ; } public void setContent(String content){ this.content = content ; } public String getName(){ return this.name ; } public String getContent(){ return this.content ; } } /**生產者線程 */ class Producer implements Runnable{ private Info info = null ; // 保存Info引用 public Producer(Info info){ this.info = info ; } public void run(){ boolean flag = true ; // 定義標記位 for(int i=0;i<10;i++){ if(flag){ this.info.set("姓名--1","內容--1") ; flag = false ; }else{ this.info.set("姓名--2","內容--2") ; flag = true ; } } } } /**消費者線程 */ class Consumer implements Runnable{ private Info info = null ; public Consumer(Info info){ this.info = info ; } public void run(){ for(int i=0;i<10;i++){ this.info.get() ; } } }
state : 獲取鎖的標誌接口
NOde{} : 獲取鎖的線程
SHARED : 共享鎖
EXCLUSIVE : 互斥鎖
CLH同步隊列
LockSupport.park() 和 LockSupport.unpark() :阻塞和喚醒
對內存中共享數據進行操做的指令集, 自動更新共享數據, 代替了鎖
內存值V,舊的預期值A,要修改的新值B。當且僅A和內存值V相同時,將內存值V修改成B,不然什麼都不作
ABA問題
由於CAS須要在操做值的時候檢查下值有沒有發生變化,若是沒有發生變化則更新,可是若是一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,可是實際上卻變化了。ABA問題的解決思路就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那麼A-B-A 就會變成1A-2B-3A