synchronized和lock比對

前言:在上面的博客說了synchronized的一些用法,下面咱們再來看看lock,這個出現頻率也是很是高的一個。html

1:獲取Lock鎖的幾種方式

前面說了synchronized有鎖對象和鎖類對象,當某個線程獲取鎖其餘線程必須等待執行完畢纔可繼續進行,好比線程A先獲取鎖,可是出現異常致使的後果就是線程B沒法獲取鎖,會出現死鎖的狀況(http://www.cnblogs.com/LipeiNet/p/6475851.html),那麼咱們一塊兒看看Lock是如何解決的。lock有4種方式來獲取鎖緩存

1:lock.lock() 若是獲取了鎖當即返回,若是別的線程持有鎖,當前線程則一直處於休眠狀態,直到獲取鎖。此種模式和synchronized同樣可是不會出現死鎖併發

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() + "開始執行");
            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() + "開始執行");
            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();
    }
}

輸出結果很明顯其中一個value是1000000,一個是2000000,效果和synchronized是同樣的。可是若是咱們去掉lock之後的結果呢,很明顯會錯,以下圖這樣this

爲啥會出現這樣狀況呢,是因爲cpu速度極快,每次處理完畢以後並無當即把數值放入Java內存中,而是放在寫緩存區,而後由寫緩存區同步到Java內存中,這樣同樣,若是線程1計算結果是2,可是仍是到內存中,致使線程2覺得value值仍是1因此會重複計算,還有從結果咱們也能夠看出value值並非100000說明2個線程是同步執行的。spa

 2:lock.tryLock();線程

這個方法和synchronized有所不一樣,synchronized和lock都會等待直到獲取鎖。若是獲取了鎖當即返回true,若是別的線程正持有鎖,當即返回false;固然咱們能夠利用while循環一直等待,直到獲取鎖而後進行。代碼以下3d

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 + "釋放鎖!!");
                    lock.unlock();
                }
            }
        });
        t1.start();
        t2.start();
    }
}

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

4:lock.lockInterruptibly()若是獲取了鎖定當即返回,若是沒有獲取鎖定,當前線程處於休眠狀態,直到或者鎖定,或者當前線程被別的線程中斷。也就是說如何線程沒有被中斷和lock.lock()的做用同樣。可是如何線程被中斷了,那麼此時這個線程不會有任何的響應,想象這麼一個場景,線程A和線程B同時執行任務,可是必須等待線程B先執行,可是執行過程當中忽然線程A忽然被中斷,那麼這個時候就可能出現死鎖,哪怕是在finally中加入unlock,這個時候咱們就要採用lockInterruptibly()了。代碼以下htm

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已經被中斷");
            }
        });
        thread1.start();
        thread2.start();
    }
}

2:讀鎖和寫鎖

 在開發中咱們最好的願望就是寫的時候加鎖,可是讀的時候不加鎖這樣會大大的提高效率,可是採用synchronized卻沒法知足咱們的要求,若是在讀的方法面前加鎖那麼全部的讀都須要等待,若是不加鎖的話那麼若是如今A,B2個線程讀取,C線程寫入可能致使的後果就是A,B2個線程取得數據不一致,明明同一種業務場景可是獲取值卻不一樣。好了lock的讀鎖和寫鎖幫助咱們實現這種功能。對象

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

運行結果:

從這個結果咱們很明顯的能夠總結讀鎖和寫鎖

第一:若是執行寫的時候,讀和寫必須等待

第二:若是執行讀的時候,寫必須等待,而讀卻不用等待

也就是說讀和寫必須存在前後順序,不論是先讀仍是先寫。

3:總結

相同點:lock能實現synchronized全部能夠實現的

不一樣點:

1:lock不容易出現死鎖,而synchronized若是某個線程出現異常就會產生死鎖

2:lock更加靈活,能夠經過tryLock來驗證是否獲取鎖,在線程中斷也一樣能夠處理

3:lock有讀寫鎖在併發量大的時候具備很大的優點,由於讀的狀況通常會比寫多不少

相關文章
相關標籤/搜索