1. 程式人生 > >java多線程系列(四)

java多線程系列(四)

ref sync 寫博客 nal 技術 分享 wait方法 而後 讀寫

Lock的使用

前言:本系列將從零開始講解java多線程相關的技術,內容參考於《java多線程核心技術》與《java並發編程實戰》等相關資料,希望站在巨人的肩膀上,再通過我的理解能讓知識更加簡單易懂。

目錄

  • 認識cpu、核心與線程
  • java多線程系列(一)之java多線程技能
  • java多線程系列(二)之對象變量的並發訪問
  • java多線程系列(三)之等待通知機制
  • java多線程系列(四)之ReentrantLock的使用

ReentrantLock(重入鎖)

public class MyService {

    private Lock lock = new ReentrantLock();

    public void testMethod() {
        lock.lock();
        for (int i = 0; i < 5; i++) {
            System.out.println("ThreadName=" + Thread.currentThread().getName()
                    + (" " + (i + 1)));
        }
        lock.unlock();
    }

}

效果和synchronized一樣,都可以同步執行,lock方法獲得鎖,unlock方法釋放鎖

await方法

public class MyService {

    private Lock lock = new ReentrantLock();
    private Condition condition=lock.newCondition();
    public void testMethod() {
        
        try {
            lock.lock();
            System.out.println("開始wait");
            condition.await();
            for (int i = 0; i < 5; i++) {
                System.out.println("ThreadName=" + Thread.currentThread().getName()
                        + (" " + (i + 1)));
            }
        } catch (InterruptedException e) {
            // TODO 自動生成的 catch 塊
            e.printStackTrace();
        }
        finally
        {
            lock.unlock();
        }
    }

}
  • 通過創建Condition對象來使線程wait,必須先執行lock.lock方法獲得鎖

signal方法

public void signal()
    {
        try
        {
            lock.lock();
            condition.signal();
        }
        finally
        {
            lock.unlock();
        }
    }
  • condition對象的signal方法可以喚醒wait線程

創建多個condition對象

  • 一個condition對象的signal(signalAll)方法和該對象的await方法是一一對應的,也就是一個condition對象的signal(signalAll)方法不能喚醒其他condition對象的await方法
  • ReentrantLock類可以喚醒指定條件的線程,而object的喚醒是隨機的

Condition類和Object類

  • Condition類的awiat方法和Object類的wait方法等效
  • Condition類的signal方法和Object類的notify方法等效
  • Condition類的signalAll方法和Object類的notifyAll方法等效

Lock的公平鎖和非公平鎖

Lock lock=new ReentrantLock(true);//公平鎖
Lock lock=new ReentrantLock(false);//非公平鎖
  • 公平鎖指的是線程獲取鎖的順序是按照加鎖順序來的,而非公平鎖指的是搶鎖機制,先lock的線程不一定先獲得鎖。

ReentrantLock類的方法

  • getHoldCount() 查詢當前線程保持此鎖的次數,也就是執行此線程執行lock方法的次數
  • getQueueLength()返回正等待獲取此鎖的線程估計數,比如啟動10個線程,1個線程獲得鎖,此時返回的是9
  • getWaitQueueLength(Condition condition)返回等待與此鎖相關的給定條件的線程估計數。比如10個線程,用同一個condition對象,並且此時這10個線程都執行了condition對象的await方法,那麽此時執行此方法返回10
  • hasWaiters(Condition condition)查詢是否有線程等待與此鎖有關的給定條件(condition),對於指定contidion對象,有多少線程執行了condition.await方法
  • hasQueuedThread(Thread thread)查詢給定線程是否等待獲取此鎖
  • hasQueuedThreads()是否有線程等待此鎖
  • isFair()該鎖是否公平鎖
  • isHeldByCurrentThread() 當前線程是否保持鎖鎖定,線程的執行lock方法的前後分別是false和true
  • isLock()此鎖是否有任意線程占用
  • lockInterruptibly()如果當前線程未被中斷,獲取鎖
  • tryLock()嘗試獲得鎖,僅在調用時鎖未被線程占用,獲得鎖
  • tryLock(long timeout TimeUnit unit)如果鎖在給定等待時間內沒有被另一個線程保持,則獲取該鎖

tryLock和lock和lockInterruptibly的區別

  • tryLock能獲得鎖就返回true,不能就立即返回false,tryLock(long timeout,TimeUnit unit),可以增加時間限制,如果超過該時間段還沒獲得鎖,返回false
  • lock能獲得鎖就返回true,不能的話一直等待獲得鎖
  • lock和lockInterruptibly,如果兩個線程分別執行這兩個方法,但此時中斷這兩個線程,前者不會拋出異常,而後者會拋出異常

讀寫鎖

  • ReentrantLock是完全互斥排他的,這樣其實效率是不高的。
public void read() {
        try {
            try {
                lock.readLock().lock();
                System.out.println("獲得讀鎖" + Thread.currentThread().getName()
                        + " " + System.currentTimeMillis());
                Thread.sleep(10000);
            } finally {
                lock.readLock().unlock();
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
  • 讀鎖,此時多個線程可以或得讀鎖
public void write() {
        try {
            try {
                lock.writeLock().lock();
                System.out.println("獲得寫鎖" + Thread.currentThread().getName()
                        + " " + System.currentTimeMillis());
                Thread.sleep(10000);
            } finally {
                lock.writeLock().unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  • 寫鎖,此時只有一個線程能獲得寫鎖
public void read() {
        try {
            try {
                lock.readLock().lock();
                System.out.println("獲得讀鎖" + Thread.currentThread().getName()
                        + " " + System.currentTimeMillis());
                Thread.sleep(10000);
            } finally {
                lock.readLock().unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void write() {
        try {
            try {
                lock.writeLock().lock();
                System.out.println("獲得寫鎖" + Thread.currentThread().getName()
                        + " " + System.currentTimeMillis());
                Thread.sleep(10000);
            } finally {
                lock.writeLock().unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  • 結果是讀鎖釋放後才執行寫鎖的方法,說明讀鎖和寫鎖是互斥的

總結

  • Lock類也可以實現線程同步,而Lock獲得鎖需要執行lock方法,釋放鎖需要執行unLock方法
  • Lock類可以創建Condition對象,Condition對象用來是線程等待和喚醒線程,需要註意的是Condition對象的喚醒的是用同一個Condition執行await方法的線程,所以也就可以實現喚醒指定類的線程
  • Lock類分公平鎖和不公平鎖,公平鎖是按照加鎖順序來的,非公平鎖是不按順序的,也就是說先執行lock方法的鎖不一定先獲得鎖
  • Lock類有讀鎖和寫鎖,讀讀共享,寫寫互斥,讀寫互斥

我覺得分享是一種精神,分享是我的樂趣所在,不是說我覺得我講得一定是對的,我講得可能很多是不對的,但是我希望我講的東西是我人生的體驗和思考,是給很多人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引發自己內心的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)

java多線程系列(四)