欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程基礎——Lock類

 更新時間:2017年02月03日 08:57:59   作者:byhieg  
Lock類是Java類來提供的功能,豐富的api使得Lock類的同步功能比synchronized的同步更強大。本文對此進行詳細介紹,下面跟著小編一起來看下吧

之前已經(jīng)說道,JVM提供了synchronized關鍵字來實現(xiàn)對變量的同步訪問以及用wait和notify來實現(xiàn)線程間通信。在jdk1.5以后,JAVA提供了Lock類來實現(xiàn)和synchronized一樣的功能,并且還提供了Condition來顯示線程間通信。

Lock類是Java類來提供的功能,豐富的api使得Lock類的同步功能比synchronized的同步更強大。本文章的所有代碼均在Lock類例子的代碼

本文主要介紹一下內(nèi)容:

  1. Lock類
  2. Lock類其他功能
  3. Condition類
  4. Condition類其他功能
  5. 讀寫鎖

Lock類

Lock類實際上是一個接口,我們在實例化的時候實際上是實例化實現(xiàn)了該接口的類Lock lock = new ReentrantLock();。用synchronized的時候,synchronized可以修飾方法,或者對一段代碼塊進行同步處理。

前面講過,針對需要同步處理的代碼設置對象監(jiān)視器,比整個方法用synchronized修飾要好。Lock類的用法也是這樣,通過Lock對象lock,用lock.lock來加鎖,用lock.unlock來釋放鎖。在兩者中間放置需要同步處理的代碼。

具體的例子如下:

public class MyConditionService {
 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();
 }
}

測試的代碼如下:

  MyConditionService service = new MyConditionService();
  new Thread(service::testMethod).start();
  new Thread(service::testMethod).start();
  new Thread(service::testMethod).start();
  new Thread(service::testMethod).start();
  new Thread(service::testMethod).start();

  Thread.sleep(1000 * 5);

結果太長就不放出來,具體可以看我源碼。總之,就是每個線程的打印1-5都是同步進行,順序沒有亂。

通過下面的例子,可以看出Lock對象加鎖的時候也是一個對象鎖,持續(xù)對象監(jiān)視器的線程才能執(zhí)行同步代碼,其他線程只能等待該線程釋放對象監(jiān)視器。

public class MyConditionMoreService {
 private Lock lock = new ReentrantLock();
 public void methodA(){
  try{
   lock.lock();
   System.out.println("methodA begin ThreadName=" + Thread.currentThread().getName() +
     " time=" + System.currentTimeMillis());
   Thread.sleep(1000 * 5);
   System.out.println("methodA end ThreadName=" + Thread.currentThread().getName() +
     " time=" + System.currentTimeMillis());
  }catch (Exception e){
   e.printStackTrace();
  }finally {
   lock.unlock();
  }
 }
 public void methodB(){
  try{
   lock.lock();
   System.out.println("methodB begin ThreadName=" + Thread.currentThread().getName() +
     " time=" + System.currentTimeMillis());
   Thread.sleep(1000 * 5);
   System.out.println("methodB end ThreadName=" + Thread.currentThread().getName() +
     " time=" + System.currentTimeMillis());
  }catch (Exception e){
   e.printStackTrace();
  }finally {
   lock.unlock();
  }
 }
}

測試代碼如下:

 public void testMethod() throws Exception {
  MyConditionMoreService service = new MyConditionMoreService();
  ThreadA a = new ThreadA(service);
  a.setName("A");
  a.start();
  ThreadA aa = new ThreadA(service);
  aa.setName("AA");
  aa.start();
  ThreadB b = new ThreadB(service);
  b.setName("B");
  b.start();
  ThreadB bb = new ThreadB(service);
  bb.setName("BB");
  bb.start();
  Thread.sleep(1000 * 30);
 } 
public class ThreadA extends Thread{
 private MyConditionMoreService service;
 public ThreadA(MyConditionMoreService service){
  this.service = service;
 }
 @Override
 public void run() {
  service.methodA();
 }
}
public class ThreadB extends Thread{
 private MyConditionMoreService service;
 public ThreadB(MyConditionMoreService service){
  this.service = service;
 }
 @Override
 public void run() {
  super.run();
  service.methodB();
 }
}

結果如下:

methodA begin ThreadName=A time=1485590913520
methodA end ThreadName=A time=1485590918522
methodA begin ThreadName=AA time=1485590918522
methodA end ThreadName=AA time=1485590923525
methodB begin ThreadName=B time=1485590923525
methodB end ThreadName=B time=1485590928528
methodB begin ThreadName=BB time=1485590928529
methodB end ThreadName=BB time=1485590933533

可以看出Lock類加鎖確實是對象鎖。針對同一個lock對象執(zhí)行的lock.lock是獲得對象監(jiān)視器的線程才能執(zhí)行同步代碼 其他線程都要等待。

在這個例子中,加鎖,和釋放鎖都是在try-finally。這樣的好處是在任何異常發(fā)生的情況下,都能保障鎖的釋放。

Lock類其他的功能

如果Lock類只有l(wèi)ock和unlock方法也太簡單了,Lock類提供了豐富的加鎖的方法和對加鎖的情況判斷。主要有

  • 實現(xiàn)鎖的公平
  • 獲取當前線程調(diào)用lock的次數(shù),也就是獲取當前線程鎖定的個數(shù)
  • 獲取等待鎖的線程數(shù)
  • 查詢指定的線程是否等待獲取此鎖定
  • 查詢是否有線程等待獲取此鎖定
  • 查詢當前線程是否持有鎖定
  • 判斷一個鎖是不是被線程持有
  • 加鎖時如果中斷則不加鎖,進入異常處理
  • 嘗試加鎖,如果該鎖未被其他線程持有的情況下成功

實現(xiàn)公平鎖

在實例化鎖對象的時候,構造方法有2個,一個是無參構造方法,一個是傳入一個boolean變量的構造方法。當傳入值為true的時候,該鎖為公平鎖。默認不傳參數(shù)是非公平鎖。

公平鎖:按照線程加鎖的順序來獲取鎖

非公平鎖:隨機競爭來得到鎖

此外,JAVA還提供isFair()來判斷一個鎖是不是公平鎖。

獲取當前線程鎖定的個數(shù)

Java提供了getHoldCount()方法來獲取當前線程的鎖定個數(shù)。所謂鎖定個數(shù)就是當前線程調(diào)用lock方法的次數(shù)。一般一個方法只會調(diào)用一個lock方法,但是有可能在同步代碼中還有調(diào)用了別的方法,那個方法內(nèi)部有同步代碼。這樣,getHoldCount()返回值就是大于1。

下面的方法用來判斷等待鎖的情況

獲取等待鎖的線程數(shù)

Java提供了getQueueLength()方法來得到等待鎖釋放的線程的個數(shù)。

查詢指定的線程是否等待獲取此鎖定

Java提供了hasQueuedThread(Thread thread)查詢該Thread是否等待該lock對象的釋放。

查詢是否有線程等待獲取此鎖定

同樣,Java提供了一個簡單判斷是否有線程在等待鎖釋放即hasQueuedThreads()。

下面的方法用來判斷持有鎖的情況

查詢當前線程是否持有鎖定

Java不僅提供了判斷是否有線程在等待鎖釋放的方法,還提供了是否當前線程持有鎖即isHeldByCurrentThread(),判斷當前線程是否有此鎖定。

判斷一個鎖是不是被線程持有

同樣,Java提供了簡單判斷一個鎖是不是被一個線程持有,即isLocked()

下面的方法用來實現(xiàn)多種方式加鎖

加鎖時如果中斷則不加鎖,進入異常處理

Lock類提供了多種選擇的加鎖方法,lockInterruptibly()也可以實現(xiàn)加鎖,但是當線程被中斷的時候,就會加鎖失敗,進行異常處理階段。一般這種情況出現(xiàn)在該線程已經(jīng)被打上interrupted的標記了。

嘗試加鎖,如果該鎖未被其他線程持有的情況下成功

Java提供了tryLock()方法來進行嘗試加鎖,只有該鎖未被其他線程持有的基礎上,才會成功加鎖。

上面介紹了Lock類來實現(xiàn)代碼的同步處理,下面介紹Condition類來實現(xiàn)wait/notify機制。

Condition類

Condition是Java提供了來實現(xiàn)等待/通知的類,Condition類還提供比wait/notify更豐富的功能,Condition對象是由lock對象所創(chuàng)建的。但是同一個鎖可以創(chuàng)建多個Condition的對象,即創(chuàng)建多個對象監(jiān)視器。這樣的好處就是可以指定喚醒線程。notify喚醒的線程是隨機喚醒一個。

下面,看一個例子,顯示簡單的等待/通知

public class ConditionWaitNotifyService {
 private Lock lock = new ReentrantLock();
 public Condition condition = lock.newCondition();
 public void await(){
  try{
   lock.lock();
   System.out.println("await的時間為 " + System.currentTimeMillis());
   condition.await();
   System.out.println("await結束的時間" + System.currentTimeMillis());
  }catch (Exception e){
   e.printStackTrace();
  }finally {
   lock.unlock();
  }
 }
 public void signal(){
  try{
   lock.lock();
   System.out.println("sign的時間為" + System.currentTimeMillis());
   condition.signal();
  }finally {
   lock.unlock();
  }
 }
}

測試的代碼如下:

  ConditionWaitNotifyService service = new ConditionWaitNotifyService();
  new Thread(service::await).start();
  Thread.sleep(1000 * 3);
  service.signal();
  Thread.sleep(1000);

結果如下:

await的時間為 1485610107421
sign的時間為1485610110423
await結束的時間1485610110423

condition對象通過lock.newCondition()來創(chuàng)建,用condition.await()來實現(xiàn)讓線程等待,是線程進入阻塞。用condition.signal()來實現(xiàn)喚醒線程。喚醒的線程是用同一個conditon對象調(diào)用await()方法而進入阻塞。并且和wait/notify一樣,await()和signal()也是在同步代碼區(qū)內(nèi)執(zhí)行。

此外看出await結束的語句是在獲取通知之后才執(zhí)行,確實實現(xiàn)了wait/notify的功能。下面這個例子是展示喚醒制定的線程。

  ConditionAllService service = new ConditionAllService();
  Thread a = new Thread(service::awaitA);
  a.setName("A");
  a.start();
  Thread b = new Thread(service::awaitB);
  b.setName("B");
  b.start();
  Thread.sleep(1000 * 3);
  service.signAAll();
  Thread.sleep(1000 * 4);

結果如下:

begin awaitA時間為 1485611065974ThreadName=A
begin awaitB時間為 1485611065975ThreadName=B
signAll的時間為1485611068979ThreadName=main
end awaitA時間為1485611068979ThreadName=A

該結果確實展示用同一個condition對象來實現(xiàn)等待通知。

對于等待/通知機制,簡化而言,就是等待一個條件,當條件不滿足時,就進入等待,等條件滿足時,就通知等待的線程開始執(zhí)行。為了實現(xiàn)這種功能,需要進行wait的代碼部分與需要進行通知的代碼部分必須放在同一個對象監(jiān)視器里面。執(zhí)行才能實現(xiàn)多個阻塞的線程同步執(zhí)行代碼,等待與通知的線程也是同步進行。對于wait/notify而言,對象監(jiān)視器與等待條件結合在一起 即synchronized(對象)利用該對象去調(diào)用wait以及notify。但是對于Condition類,是對象監(jiān)視器與條件分開,Lock類來實現(xiàn)對象監(jiān)視器,condition對象來負責條件,去調(diào)用await以及signal。

Condition類的其他功能

和wait類提供了一個最長等待時間,awaitUntil(Date deadline)在到達指定時間之后,線程會自動喚醒。但是無論是await或者awaitUntil,當線程中斷時,進行阻塞的線程會產(chǎn)生中斷異常。Java提供了一個awaitUninterruptibly的方法,使即使線程中斷時,進行阻塞的線程也不會產(chǎn)生中斷異常。

讀寫鎖

Lock類除了提供了ReentrantLock的鎖以外,還提供了ReentrantReadWriteLock的鎖。讀寫鎖分成兩個鎖,一個鎖是讀鎖,一個鎖是寫鎖。讀鎖與讀鎖之間是共享的,讀鎖與寫鎖之間是互斥的,寫鎖與寫鎖之間也是互斥的。

看下面的讀讀共享的例子:

public class ReadReadService {
 private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 public void read(){
  try{
   try{
    lock.readLock().lock();
    System.out.println("獲得讀鎖" + Thread.currentThread().getName() +
    " " + System.currentTimeMillis());
    Thread.sleep(1000 * 10);
   }finally {
    lock.readLock().unlock();
   }
  }catch (InterruptedException e){
   e.printStackTrace();
  }
 }
}

測試的代碼和結果如下:

  ReadReadService service = new ReadReadService();
  Thread a = new Thread(service::read);
  a.setName("A");
  Thread b = new Thread(service::read);
  b.setName("B");
  a.start();
  b.start();

結果如下:

獲得讀鎖A 1485614976979
獲得讀鎖B 1485614976981

兩個線程幾乎同時執(zhí)行同步代碼。

下面的例子是寫寫互斥的例子

public class WriteWriteService {
 private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 public void write(){
  try{
   try{
    lock.writeLock().lock();
    System.out.println("獲得寫鎖" + Thread.currentThread().getName() +
      " " +System.currentTimeMillis());
    Thread.sleep(1000 * 10);
   }finally {
    lock.writeLock().unlock();
   }
  }catch (InterruptedException e){
   e.printStackTrace();
  }
 }
}

測試代碼和結果如下:

  WriteWriteService service = new WriteWriteService();
  Thread a = new Thread(service::write);
  a.setName("A");
  Thread b = new Thread(service::write);
  b.setName("B");
  a.start();
  b.start();
  Thread.sleep(1000 * 30);

結果如下:

獲得寫鎖A 1485615316519
獲得寫鎖B 1485615326524

兩個線程同步執(zhí)行代碼

讀寫互斥的例子:

public class WriteReadService {
 private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 public void read(){
  try{
   try{
    lock.readLock().lock();
    System.out.println("獲得讀鎖" + Thread.currentThread().getName()
      + " " + System.currentTimeMillis());
    Thread.sleep(1000 * 10);
   }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(1000 * 10);
   }finally {
    lock.writeLock().unlock();
   }
  }catch (InterruptedException e){
   e.printStackTrace();
  }
 }
}

測試的代碼如下:

  WriteReadService service = new WriteReadService();
  Thread a = new Thread(service::write);
  a.setName("A");
  a.start();
  Thread.sleep(1000);
  Thread b = new Thread(service::read);
  b.setName("B");
  b.start();
  Thread.sleep(1000 * 30);

結果如下:

獲得寫鎖A 1485615633790
獲得讀鎖B 1485615643792

兩個線程讀寫之間也是同步執(zhí)行代碼。

總結

本文介紹了新的同步代碼的方式Lock類以及新的等待/通知機制的實現(xiàn)Condition類。本文只是很簡單的介紹了他們的概念和使用的方式。關于Condition以及讀寫鎖還有更多的內(nèi)容,將放在以后的博客中。

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關文章

  • Java數(shù)據(jù)結構二叉樹難點解析

    Java數(shù)據(jù)結構二叉樹難點解析

    樹是一種重要的非線性數(shù)據(jù)結構,直觀地看,它是數(shù)據(jù)元素(在樹中稱為結點)按分支關系組織起來的結構,很象自然界中的樹那樣。樹結構在客觀世界中廣泛存在,如人類社會的族譜和各種社會組織機構都可用樹形象表示
    2021-10-10
  • MyBatis Plus整合Redis實現(xiàn)分布式二級緩存的問題

    MyBatis Plus整合Redis實現(xiàn)分布式二級緩存的問題

    Mybatis內(nèi)置的二級緩存在分布式環(huán)境下存在分布式問題,無法使用,但是我們可以整合Redis來實現(xiàn)分布式的二級緩存,這篇文章給大家介紹MyBatis Plus整合Redis實現(xiàn)分布式二級緩存,感興趣的朋友跟隨小編一起看看吧
    2023-11-11
  • Java?properties?和?yml?的區(qū)別解析

    Java?properties?和?yml?的區(qū)別解析

    properties和yml都是Spring?Boot支持的兩種配置文件,它們可以看做Spring?Boot在不同時期的兩種“產(chǎn)品”,這篇文章主要介紹了Java?properties?和?yml?的區(qū)別,需要的朋友可以參考下
    2023-02-02
  • java swing編程入門代碼編寫(java編程入門)

    java swing編程入門代碼編寫(java編程入門)

    Swing是一個為Java設計的GUI工具包,是用來做UI界面的,大家看了下面的介紹就要吧自己做java界面了
    2013-12-12
  • mybatis中的緩存問題解析

    mybatis中的緩存問題解析

    本篇文章主要介紹了mybatis中的緩存問題解析,詳細的介紹了關于mybatis的一級緩存和二級緩存,具有一定的參考價值,有興趣的可以了解一下。
    2017-04-04
  • Spring Boot+Nginx實現(xiàn)大文件下載功能

    Spring Boot+Nginx實現(xiàn)大文件下載功能

    相信很多小伙伴,在日常開放中都會遇到大文件下載的情況,大文件下載方式也有很多,比如非常流行的分片下載、斷點下載;當然也可以結合Nginx來實現(xiàn)大文件下載,在中小項目非常適合使用,這篇文章主要介紹了Spring Boot結合Nginx實現(xiàn)大文件下載,需要的朋友可以參考下
    2024-05-05
  • Java生成日期時間存入Mysql數(shù)據(jù)庫的實現(xiàn)方法

    Java生成日期時間存入Mysql數(shù)據(jù)庫的實現(xiàn)方法

    本文主要介紹了Java生成日期時間存入Mysql數(shù)據(jù)庫的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • java多線程之CyclicBarrier的使用方法

    java多線程之CyclicBarrier的使用方法

    這篇文章主要介紹了java多線程之CyclicBarrier的使用方法的相關資料,希望通過本文能幫助到大家,讓大家理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • mybatis映射內(nèi)部類的使用及注意事項說明

    mybatis映射內(nèi)部類的使用及注意事項說明

    這篇文章主要介紹了mybatis映射內(nèi)部類的使用及注意事項說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 解決java讀取EXCEL數(shù)據(jù)變成科學計數(shù)法的問題

    解決java讀取EXCEL數(shù)據(jù)變成科學計數(shù)法的問題

    這篇文章主要介紹了解決java讀取EXCEL數(shù)據(jù)變成科學計數(shù)法的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04

最新評論