20200428 Java中Lock和synchronized的比較和應用

synchronized在語法上已經足夠簡單了,在JDK 5之前只能藉助此實現,但是由於是獨佔鎖,性能卻不高,因此JDK 5以後就開始藉助於JNI來完成更高級的鎖實現。

JDK 5中的鎖是接口java.util.concurrent.locks.Lock。

另外java.util.concurrent.locks.ReadWriteLock提供了一對可供讀寫併發的鎖。

線程獲取鎖和釋放鎖的方法。

java.util.concurrent.locks.Lock類,既然是鎖,肯定有用於線程獲取鎖和釋放鎖的方法存在,這兩個方法爲:

1、void lock();

函數功能:獲取鎖。如果鎖不可用,由於線程調度目的,將禁用此線程,並且在獲得鎖之前,該線程將一直處於休眠狀態。

2、unlock();

函數功能:釋放鎖。對應於所有的獲取鎖,例如lock()、tryLock()、tryLock(xx)、lockInterruptibly()等操作,如果成功的話應該對應着一個unlock(),這樣可以避免死鎖或者資源浪費。

 

join() 在實際使用過程中,我們可以通過join方法來等待線程執行的結果,其實有點類似future/callable的功能。

出現了數據重複問題,明顯可見存在線程安全問題;

add()方法中 a++; 並不是一個原子性操作,我們使用原子類把int a變成一個原子類。我們對數值的一些非原子性操作,都可以使用原子類轉化爲原子性操作。

 

1、使用Lock同步來模擬AtomicInteger類

public class AtomicIntegerLock {

    private volatile int value;

    //獲取一個鎖實例
    private Lock lock = new ReentrantLock();

    public AtomicIntegerLock(int value) {
        this.value = value;
    }

    //同一時刻只能有一個線程修改值
    public void set(int newValue) {
        lock.lock();
        try {
            this.value = newValue;
        } finally {
            lock.unlock();
        }
    }

    //獲取值
    public final int get() {
        lock.lock();
        try {
            return value;
        } finally {
            lock.unlock();
        }
    }

    public final int getAndSet(int newValue) {
        lock.lock();
        try {
            int oldValue = value;
            value = newValue;
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 返回舊值並增加
     *
     * @param delta
     * @return
     */
    public final int getAndAdd(int delta) {
        lock.lock();
        try {
            int oldValue = value;
            value += delta;
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 先增加再返回新值
     *
     * @param delta
     * @return
     */
    public final int addAndGet(int delta) {
        lock.lock();
        try {
            value += delta;
            return value;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 比較是否可預期值相同
     *
     * @param expect
     * @param newValue
     * @return
     */
    public final boolean getAndCompare(int expect, int newValue) {
        lock.lock();
        try {
            if (this.value == expect) {
                value = newValue;
                return true;
            } else {
                return false;
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 先獲取value的值,在遞增1;
     * @return
     */
    public final int getAndIncrement(){
        lock.lock();
        try {
            return value++;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 先獲取value的值,然後減1
     * @return
     */
    public final int getAndDecrement(){
        lock.lock();
        try {
            return value--;
        } finally {
            lock.unlock();
        }
    }


    /**
     * 先自增1,在返回新值
     * @return
     */
    public final int incrementAndGet(){
        lock.lock();
        try {
            return ++value;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 先遞減1,在返回值
     * @return
     */
    public final int decrementAndGet(){
        lock.lock();
        try {
            return --value;
        } finally {
            lock.unlock();
        }
    }

    public final String toString(){
        lock.lock();
        return  Integer.toString(get());
    }
}

2、Lock同步和synchronized同步兩種鎖的性能:使用lock的性能要好。與其說ReentrantLock性能好,還不如說synchronized還有很大優化的餘地。

3、用Lock來進行同步計數和使用AtomicInteger類計數的性能比較;使用CAS指令確實更要快的多。

/**
 * Created on 2020/4/28 16:30
 * author:crs
 * Description:測試兩種同步性能的差別
 */
public class TestAtomicIntegerLock {

    private static int synValue = 0;

    public static void main(String[] args) {
        int threadNum = 10;
        int maxValue = 1000000;

        Thread[] t = new Thread[threadNum];
        //返回的是納秒 //System.nanoTime();
        Long begin = System.currentTimeMillis();
        for (int i = 0; i <threadNum ; i++) {
            AtomicIntegerLock aIL = new AtomicIntegerLock(0);
            //創建了十個線程,每個線程的任務確定
            t[i]=new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int j=0;j<maxValue;j++){
                        aIL.getAndIncrement();
                    }
                }
            });
        }
        for(int i=0;i<threadNum;i++){
            t[i].start();
        }

        //main線程等待前面開啓的所有線程結束
        for(int i=0;i<threadNum;i++){
            try {
                t[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("使用lock所花費的時間爲:"+(System.currentTimeMillis()-begin));

        int[] lock = new int[0];
        begin = System.currentTimeMillis();
        for(int i=0;i<threadNum;i++){
            synValue = 0;
            t[i]=new Thread(new Runnable(){

                @Override
                public void run() {
                    for(int j=0;j<maxValue;j++){
                        synchronized(lock){
                            ++synValue;
                        }
                    }
                }
            });
        }
        for(int i=0;i<threadNum;i++){
            t[i].start();
        }
        //main線程等待前面開啓的所有線程結束
        for(int i=0;i<threadNum;i++){
            try {
                t[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(synValue);
        System.out.println("使用synchronized所花費的時間爲:"+(System.currentTimeMillis()-begin));
        
        
        begin = System.currentTimeMillis();
        for(int i=0;i<threadNum;i++){
            AtomicInteger ai = new AtomicInteger(0);
            t[i]=new Thread(new Runnable(){

                @Override
                public void run() {
                    for(int j=0;j<maxValue;j++){
                        ai.incrementAndGet();
                    }
                }

            });
        }
        for(int i=0;i<threadNum;i++){
            t[i].start();
        }
        //main線程等待前面開啓的所有線程結束
        for(int i=0;i<threadNum;i++){
            try {
                t[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("使用原子操作類AtomicInteger所花費的時間爲:"+(System.currentTimeMillis()-begin));
    }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章