1:獲取Lock鎖的幾種方式

前面說了synchronized有鎖對象和鎖類對象,當某個線程獲取鎖其他線程必須等待執(zhí)行完畢才可繼續(xù)進行,比如線程A先獲取鎖,但是出現(xiàn)異常導致的后果就是線程B無法獲取鎖,會出現(xiàn)死鎖的情況(http://www.cnblogs.com/LipeiNet/p/6475851.html),那么我們一起看看Lock是如何解決的。lock有4種方式來獲取鎖

1:lock.lock() 如果獲取了鎖立即返回,如果別的線程持有鎖,當前線程則一直處于休眠狀態(tài),直到獲取鎖。此種模式和synchronized一樣但是不會出現(xiàn)死鎖

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

public class Lock1 {    static int value = 0;    static Lock lock = new ReentrantLock();    static class Task1 implements Runnable {        public void run() {
            System.out.println("線程" + Thread.currentThread().getName() + "開始執(zhí)行");
            lock.lock();            try {                for (int i = 0; i < 1000000; i++) {
                    value++;
                }
                System.out.println(value);
            } finally {
                lock.unlock();
            }
        }
    }    static class Task2 implements Runnable {        public void run() {
            System.out.println("線程" + Thread.currentThread().getName() + "開始執(zhí)行");
            lock.lock();            try {                for (int i = 0; i < 1000000; i++) {
                    value++;
                }
                System.out.println(value);
            } finally {
                lock.unlock();
            }
        }
    }    public static void main(String[] args) {
        ExecutorService service= Executors.newCachedThreadPool();
        service.execute(new Task1());
        service.execute(new Task2());
        service.shutdown();
    }
}

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

輸出結(jié)果很明顯其中一個value是1000000,一個是2000000,效果和synchronized是一樣的。但是如果我們?nèi)サ鬺ock以后的結(jié)果呢,很明顯會錯,如下圖這樣

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

為啥會出現(xiàn)這樣情況呢,是由于cpu速度極快,每次處理完畢之后并沒有立即把數(shù)值放入Java內(nèi)存中,而是放在寫緩存區(qū),然后由寫緩存區(qū)同步到Java內(nèi)存中,這樣一樣,如果線程1計算結(jié)果是2,但是還是到內(nèi)存中,導致線程2以為value值還是1所以會重復計算,還有從結(jié)果我們也可以看出value值并不是100000說明2個線程是同步執(zhí)行的。

 2:lock.tryLock();

這個方法和synchronized有所不同,synchronized和lock都會等待直到獲取鎖。如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false;當然我們可以利用while循環(huán)一直等待,直到獲取鎖然后進行。代碼如下

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

public class Lock2 {    public static void main(String[] args) {        final Lock lock = new ReentrantLock();
        Thread t1 = new Thread(new Runnable() {            public void run() {
                String tName = Thread.currentThread().getName();                while (!lock.tryLock()) {                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("等待獲取鎖");
                }                try {                    for (int i = 0; i < 5; i++) {
                        System.out.println(tName + ":" + i);
                    }
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {            public void run() {
                String tName = Thread.currentThread().getName();                while (!lock.tryLock()) {                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("等待獲取鎖");
                }                try {                    for (int i = 0; i < 5; i++) {
                        System.out.println(tName + ":" + i);
                    }

                } catch (Exception e) {
                    System.out.println(tName + "出錯了?。?!");
                } finally {
                    System.out.println(tName + "釋放鎖?。?quot;);
                    lock.unlock();
                }
            }
        });
        t1.start();
        t2.start();
    }
}

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

3:lock.trylock(long time, TimeUnit unit)如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會等待參數(shù)給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false;unit是time的時間單位比如TimeUnit.SECONDS就是表示秒

4:lock.lockInterruptibly()如果獲取了鎖定立即返回,如果沒有獲取鎖定,當前線程處于休眠狀態(tài),直到或者鎖定,或者當前線程被別的線程中斷。也就是說如何線程沒有被中斷和lock.lock()的作用一樣。但是如何線程被中斷了,那么此時這個線程不會有任何的響應,想象這么一個場景,線程A和線程B同時執(zhí)行任務,但是必須等待線程B先執(zhí)行,但是執(zhí)行過程中突然線程A突然被中斷,那么這個時候就可能出現(xiàn)死鎖,哪怕是在finally中加入unlock,這個時候我們就要采用lockInterruptibly()了。代碼如下

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

public class Lock3 {    public static void main(String[] args) {        final Lock lock = new ReentrantLock();        final Thread thread1 = new Thread(new Runnable() {            public void run() {                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println("等待被中斷");
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    System.out.println("我被中斷了");
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {            public void run() {
                lock.lock();                try {
                    TimeUnit.SECONDS.sleep(4);
                } catch (InterruptedException e) {

                }
                thread1.interrupt();
                System.out.println("線程1已經(jīng)被中斷");
            }
        });
        thread1.start();
        thread2.start();
    }
}

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

2:讀鎖和寫鎖

 在開發(fā)中我們最好的愿望就是寫的時候加鎖,但是讀的時候不加鎖這樣會大大的提升效率,但是采用synchronized卻無法滿足我們的要求,如果在讀的方法面前加鎖那么所有的讀都需要等待,如果不加鎖的話那么如果現(xiàn)在A,B2個線程讀取,C線程寫入可能導致的后果就是A,B2個線程取得數(shù)據(jù)不一致,明明同一種業(yè)務場景但是獲取值卻不同。好了lock的讀鎖和寫鎖幫助我們實現(xiàn)這種功能。

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

public class ReadWriteLockTest {    private int value = 0;
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();    public void add(int value) {
        Lock writeLock = readWriteLock.writeLock();
        writeLock.lock();        try {
            TimeUnit.SECONDS.sleep(3);
            System.out.println("添加開始時間:" + new Date());            this.value += value;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
    }    public void getValue() {
        Lock readLock = readWriteLock.readLock();
        readLock.lock();        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("獲取開始時間:" + new Date());
            System.out.println(value);
        } catch (InterruptedException e) {

        } finally {
            readLock.unlock();
        }
    }    public static void main(String[] args) {        final ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
        Runnable task1 = new Runnable() {            public void run() {
                readWriteLockTest.add(100);
            }
        };
        Runnable task2 = new Runnable() {            public void run() {
                readWriteLockTest.getValue();
            }
        };
        ExecutorService service = Executors.newCachedThreadPool();        for (int i=0;i<2;i++){
            service.execute(task1);
        }        for (int i=0;i<2;i++){
            service.execute(task2);
        }        for (int i=0;i<2;i++){
            service.execute(task1);
        }
        service.shutdown();
    }
}

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

運行結(jié)果:大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

從這個結(jié)果我們很明顯的可以總結(jié)讀鎖和寫鎖

第一:如果執(zhí)行寫的時候,讀和寫必須等待

第二:如果執(zhí)行讀的時候,寫必須等待,而讀卻不用等待

也就是說讀和寫必須存在先后順序,不管是先讀還是先寫。

3:總結(jié)

相同點:lock能實現(xiàn)synchronized所有可以實現(xiàn)的

不同點:

1:lock不容易出現(xiàn)死鎖,而synchronized如果某個線程出現(xiàn)異常就會產(chǎn)生死鎖

2:lock更加靈活,可以通過tryLock來驗證是否獲取鎖,在線程中斷也同樣可以處理

3:lock有讀寫鎖在并發(fā)量大的時候具有很大的優(yōu)勢,因為讀的情況一般會比寫多很多