JAVA線程13 - 新特性:Lock和條件變量

1、Lock

1. 概述

Lock是JDK 1.5之後將同步和鎖封裝成了對象。Lock是對以前synchronized的替代。 
Lock接口的實現類:互斥鎖ReentrantLock 。

2. synchronized與Lock區別 

synchronized對於鎖的操做是隱式的;Lock鎖對象是顯式的。 
synchronized內只有一個鎖。Lock能夠有多個Conditoin。

3. 語法 

Lock myLock = new ReentrantLock(); 
myLock.lock();//獲取鎖 
try{ 
    //do something 
}finally{ 
    //釋放鎖。須要放在finally子句內,不然拋異常後,鎖未被釋放,其餘線程則會永遠被阻塞 
    myLock.unlock(); 
}

2、讀寫鎖

1. 概述

爲了提升性能,Java提供了讀寫鎖,在讀的地方使用讀鎖,在寫的地方使用寫鎖,靈活控制,在必定程度上提升了程序的執行效率。

讀寫鎖分爲讀鎖和寫鎖。多個讀鎖不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥。 java

Java中讀寫鎖有個接口java.util.concurrent.locks.ReadWriteLock,也有具體的實現ReentrantReadWriteLock。 數據庫

在實際開發中,最好在能用讀寫鎖的狀況下使用讀寫鎖,而不要用普通鎖,以求更好的性能。 緩存

2. 使用讀寫鎖完成一個簡單的緩存系統

public class SimpleCacheDemo{

    private Map<String, Object> cache = new HashMap<String, Object>();
    private ReadWriteLock rwlock = new ReentrantReadWriteLock();

    public Object getData(String key){
        rwlock.readLock().lock();
        Object obj = null;
        try{
            obj = cache.get(key);
            if(obj == null){
                rwlock.readLock().unlock();
                rolock.wirteLock().lock();
                try{
                    if(obj == null){
                        obj = queryDB(key);
                        if(obj != null){
                            cache.put(key, obj);
                            return obj;
                        }
                    }
                }finally{
                    rwlock.writeLock().unlock();
                }
                rwlock.readLock().lock();
            }
        }finally{
            rwlock.readLock().unlock();
        }
        return obj;
    }

    //從數據庫查出值
    public Object queryDB(key){
        //todo
        return null;
    }
}

3、條件變量

條件變量都實現了java.util.concurrent.locks.Condition接口,條件變量的實例化是經過一個Lock對象上調用newCondition()方法來獲取的,這樣,條件就和一個鎖對象綁定起來了。所以,Java中的條件變量只能和鎖配合使用,來控制併發程序訪問競爭資源的安全。  安全

條件變量的出現是爲了更精細控制線程等待與喚醒,在Java5以前,線程的等待與喚醒依靠的是Object對象的wait()和notify()/notifyAll()方法,這樣的處理不夠精細。 併發

Condition將Object監視器方法wait()、notify()、notifyAll()分解成大相徑庭的對象,以便經過這些對象與任意Lock實現組合使用。 對應的方法是:await()、signal() 、signalAll() 。

4、使用JDK1.5後的新特性對多生產者與多消費者示例優化

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** 
* Java線程:併發協做-生產者消費者模型 
*/ 
public class ProducerConsumer { 
    public static void main(String[] args) { 
        Godown godown = new Godown(30); 
        Consumer c1 = new Consumer(50, godown); 
        Consumer c2 = new Consumer(20, godown); 
        Consumer c3 = new Consumer(30, godown); 
        Producer p1 = new Producer(10, godown); 
        Producer p2 = new Producer(10, godown); 
        Producer p3 = new Producer(50, godown); 
        Producer p4 = new Producer(10, godown); 
        Producer p5 = new Producer(70, godown); 


        c1.start(); 
        c2.start(); 
        c3.start(); 
        p1.start(); 
        p2.start(); 
        p3.start(); 
        p4.start(); 
        p5.start(); 
    } 
} 


/** 
* 倉庫 
*/ 
class Godown { 
    public static final int max_size = 100; //最大庫存量 
    public int curnum;     //當前庫存量 
    
    private final Lock lock = new ReentrantLock();
    private final Condition connProduce = lock.newCondition();
    private final Condition connConsume = lock.newCondition();
    
    public Godown() { 
    } 

    public Godown(int curnum) { 
        this.curnum = curnum; 
    } 

    /** 
     * 生產指定數量的產品 
     * 
     * @param neednum 
     */ 
    public void produce(int neednum) { 
    lock.lock();
    try{
       //測試是否須要生產 
       while (neednum + curnum > max_size) { 
           System.out.println("要生產的產品數量" + neednum + "超過剩餘庫存量" + (max_size - curnum) + ",暫時不能執行生產任務!"); 
           try { 
               //當前的生產線程等待 
               connProduce.await();
           } catch (InterruptedException e) { 
               e.printStackTrace(); 
           } 
       } 
       //知足生產條件,則進行生產,這裏簡單的更改當前庫存量 
       curnum += neednum; 
       System.out.println("已經生產了" + neednum + "個產品,現倉儲量爲" + curnum); 
       //喚醒在此對象監視器上等待的全部消費線程 
       connConsume.signalAll();
    }finally{
    lock.unlock();
    }
    } 

    /** 
     * 消費指定數量的產品 
     * 
     * @param neednum 
     */ 
    public void consume(int neednum) { 
    lock.lock();
    try{
       //測試是否可消費 
       while (curnum < neednum) { 
           try { 
               //當前的消費線程等待 
           	connConsume.await();
           } catch (InterruptedException e) { 
               e.printStackTrace(); 
           } 
       } 
       //知足消費條件,則進行消費,這裏簡單的更改當前庫存量 
       curnum -= neednum; 
       System.out.println("已經消費了" + neednum + "個產品,現倉儲量爲" + curnum); 
       //喚醒在此對象監視器上等待的全部生產線程 
       connProduce.signalAll();
    }finally{
    lock.unlock();
    }
    } 
} 


/** 
* 生產者 
*/ 
class Producer extends Thread { 
    private int neednum;                //生產產品的數量 
    private Godown godown;            //倉庫 

    Producer(int neednum, Godown godown) { 
        this.neednum = neednum; 
        this.godown = godown; 
    } 

    public void run() { 
        //生產指定數量的產品 
        godown.produce(neednum); 
    } 
} 


/** 
* 消費者 
*/ 
class Consumer extends Thread { 
    private int neednum;                //生產產品的數量 
    private Godown godown;            //倉庫 

    Consumer(int neednum, Godown godown) { 
        this.neednum = neednum; 
        this.godown = godown; 
    } 

    public void run() { 
        //消費指定數量的產品 
        godown.consume(neednum); 
    } 
}
相關文章
相關標籤/搜索