1. 程式人生 > >JAVA 多線程(8):synchronized 的同夥lock

JAVA 多線程(8):synchronized 的同夥lock

tel @override str fin this 通知 public 大括號 創建

Lock:
lock對象功能類似synchronized ,但是更加方便,或者說有更多的功能。

實現類:

1.ReentrantLock

2.ReentrantReadWriteLock : 讀寫互斥,比1功能再多一點

一、ReentrantLock

首先回顧一下synchronized:

public void test(){
        synchronized (this){
            System.out.println(Thread.currentThread().getName()+"begin");
            try
{ Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"end"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args){ TestRLock testRLock
= new TestRLock(); Runnable runnable = new Runnable() { @Override public void run() { testRLock.test(); } }; Thread t = new Thread(runnable); Thread t2 = new Thread(runnable); t.start(); t2.start(); }

輸出:

技術分享圖片

結果是同步的。

看一下lock 實現:

private Lock lock = new ReentrantLock();

    public void test() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

輸出結果與上面的一致。

相比synchronized個人感覺更清晰,至少不用去找大括號了。

關於lock 的wait與notify:

lock 同樣有等待/通知機制,只不過調用的方法名稱不同,並且細粒度更強。

在調用時,需要聲明一個條件對象 condition(跟wait一樣,必須在lock範圍內),如下;

private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void test() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            condition.await();
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void test2() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            condition.signal();
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                testRLock.test2();
            }
        };
        Thread t = new Thread(runnable);
        Thread t2 = new Thread(runnable2);
        t.start();
        try {
            Thread.sleep(2000);
            t2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

輸出:
技術分享圖片

wait 對應的是 await,notify 對應的是signal,notifyAll 對應的是signalAll。

區別:使用condition 的好處是可以控制通知部分線程,因為conditon 可以創建多個的緣故。

如下:

private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private Condition condition2 = lock.newCondition();

    public void test() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            condition.await();
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void test3() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            condition2.await();
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void test2() {
        lock.lock();
        condition.signal();
        System.out.println(Thread.currentThread().getName() + "喚醒了等待1");
        lock.unlock();
    }

    public static void main(String[] args) {
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                testRLock.test2();
            }
        };
        Runnable runnable3 = new Runnable() {
            @Override
            public void run() {
                testRLock.test3();
            }
        };
        Thread t = new Thread(runnable, "我是等待1");
        Thread t3 = new Thread(runnable3, "我是等待2");
        Thread t2 = new Thread(runnable2, "我來喚醒1");
        t.start();
        t3.start();
        try {
            Thread.sleep(3000);
            t2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

輸出:

技術分享圖片

例子中增加了一個conditon 實例,並且conditon2的等待是沒有人通知他的。由此實現的是通知部分等待線程。

其他功能:

1.getHoldCount() :獲取當前鎖定的個數,在unlock之前保持此鎖的個數。

2.getQueueLength():獲取等待鎖釋放的線程數

3.getWaitQueyeLenth():獲取調用await的conditon的線程數,如果有5個線程同時調用了同一個codition的await,且都沒有調用signal 釋放鎖,那麽此值為5.

4.hasQueueThread():查詢某個線程是否獲得了鎖 ,返回值為布爾類型

5.hasQueueThreads():查詢是否有線程在等待調用鎖,返回值為布爾

6.hasWaiters():查詢是否有線程在等待conditon 通知

7.isFair():判斷是否為公平鎖。 公平鎖-》是否為先進先出。在創建鎖實例時是否設定。

技術分享圖片

8.isHeldByCurrentThread():當前線程是否保持了該鎖。

9.isLocked():查詢lock是否被任意線程保持。

還有其他的一些用法,在此不一個個敲字了。。。

二、ReentrantReadWriteLock

從名字看出,它持有2把鎖,一把read鎖不互斥,也就是共享鎖,另一把write鎖互斥。

讀讀共享:

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void test() {
        lock.readLock().lock();
        System.out.println(Thread.currentThread().getName() + System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.readLock().unlock();
    }

    public void test2() {
        lock.writeLock().lock();
        System.out.println(Thread.currentThread().getName() + System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.writeLock().unlock();
    }

    public static void main(String[] args) {
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };
        Thread t = new Thread(runnable, "A");
        Thread t2 = new Thread(runnable, "B");
        t.start();
        t2.start();
    }

輸出:

技術分享圖片

寫寫互斥:

把main主線程調用中runnable run 方法改為調用test2

輸出:
技術分享圖片

由結果看出,寫鎖必須等待,也就是同步操作。

讀寫互斥(凡是有寫入操作的,都會等待)

main 方法修改為:

public static void main(String[] args) {
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                testRLock.test2();
            }
        };
        Thread t = new Thread(runnable, "A");
        Thread t2 = new Thread(runnable2, "B");
        t.start();
        t2.start();
    }

輸出:

技術分享圖片

總結:

其實如果沒有太需要的話使用synchronized 就足夠了,但是如果追求控制欲的同學可以使用lock,對麽?

JAVA 多線程(8):synchronized 的同夥lock