Java併發編程--3.Lock

Lock接口

它提供3個經常使用的鎖併發

lock() : 獲不到鎖就就一直阻塞

trylock() :獲不到鎖就馬上放回 或者 定時的,輪詢的獲取鎖 

lockInterruptibly() : 獲不到鎖時阻塞,但可接受中斷信號後退出阻塞狀態

ReentrantLock

實現機制

基於衝突的樂觀併發策略: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寫入數據…
不等了,嘗試中斷
我不讀了
讀結束

ReentrantReadWriteLock讀寫鎖

特色

互斥:它使得讀寫操做互斥,讀讀操做不互斥

鎖降級:寫線程獲取寫入鎖後能夠獲取讀取鎖,而後釋放寫入鎖,這樣就從寫入鎖變成了讀取鎖

少寫多讀的例子

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
......

 Condition條件變量

經過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() ;  
        }  
    }  
} 

AQS 和 CAS

AQS : JUC基礎類

state : 獲取鎖的標誌接口

 

NOde{} : 獲取鎖的線程
SHARED : 共享鎖
EXCLUSIVE : 互斥鎖

 

CLH同步隊列

LockSupport.park() 和 LockSupport.unpark() :阻塞和喚醒

CAS: JUC基礎理論

對內存中共享數據進行操做的指令集, 自動更新共享數據, 代替了鎖

內存值V,舊的預期值A,要修改的新值B。當且僅A和內存值V相同時,將內存值V修改成B,不然什麼都不作

ABA問題

由於CAS須要在操做值的時候檢查下值有沒有發生變化,若是沒有發生變化則更新,可是若是一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,可是實際上卻變化了。ABA問題的解決思路就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那麼A-B-A 就會變成1A-2B-3A

相關文章
相關標籤/搜索