目錄
Lock與synchronized的比較
首頁 Java java教程 Java多執行緒中Lock怎麼使用

Java多執行緒中Lock怎麼使用

May 12, 2023 pm 02:46 PM
java lock

Jdk1.5 以後,在java.util.concurrent.locks 套件下,有一組實現線程同步的接口和類,說到線程的同步,可能大家都會想到synchronized 關鍵字,

這是java 內建的關鍵字,用來處理線程同步的,但這個關鍵字有很多的缺陷,使用起來也不是很方便和直觀,所以就出現了Lock,下面,我們

就來對比著講解Lock。

通常我們在使用 synchronized 關鍵字的時候會遇到下面這些問題:

(1)不可控性,無法做到隨心所欲的加鎖和釋放鎖定。

(2)效率比較低下,例如我們現在並發的讀兩個文件,讀與讀之間是互不影響的,但如果給這個讀的對象使用synchronized 來實現同步的話,

那麼只要有一個執行緒進入了,那麼其他的執行緒都要等待。

(3)無法知道執行緒是否取得到了鎖。

而上面synchronized 的這些問題,Lock 都可以很好的解決,並且jdk1.5 以後,還提供了各種鎖,例如讀寫鎖,但有一點需要注意,使用synchronized

關鍵時,無須手動釋放鎖,但使用Lock 必須手動釋放鎖。下面我們就來學習 Lock 鎖。

Lock 是一個上層的接口,其原型如下,總共提供了 6 個方法:

public interface Lock {
  // 用来获取锁,如果锁已经被其他线程获取,则一直等待,直到获取到锁
   void lock();
  // 该方法获取锁时,可以响应中断,比如现在有两个线程,一个已经获取到了锁,另一个线程调用这个方法正在等待锁,但是此刻又不想让这个线程一直在这死等,可以通过
    调用线程的Thread.interrupted()方法,来中断线程的等待过程
  void lockInterruptibly() throws InterruptedException;
  // tryLock方法会返回bool值,该方法会尝试着获取锁,如果获取到锁,就返回true,如果没有获取到锁,就返回false,但是该方法会立刻返回,而不会一直等待
   boolean tryLock();
  // 这个方法和上面的tryLock差不多是一样的,只是会尝试指定的时间,如果在指定的时间内拿到了锁,则会返回true,如果在指定的时间内没有拿到锁,则会返回false
   boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  // 释放锁
   void unlock();
  // 实现线程通信,相当于wait和notify,后面会单独讲解
   Condition newCondition();
}
登入後複製

那麼這幾個方法該如何使用了?前面我們說到,使用Lock 是需要手動釋放鎖的,但是如果程式中拋出了異常,那麼就無法做到釋放鎖,有可能引起死鎖,

所以我們在使用Lock 的時候,有一個固定的格式,如下:

Lock l = ...;
      l.lock();
      try {
        // access the resource protected by this lock
      } finally {// 必须使用try,最后在finally里面释放锁
        l.unlock();
      }
登入後複製

下面我們來看一個簡單的例子,程式碼如下:

/**
 * 描述:Lock使用
 */
public class LockDemo {
    // new一个锁对象,注意此处必须声明成类对象,保持只有一把锁,ReentrantLock是Lock的唯一实现类
   Lock lock = new ReentrantLock();
   public void readFile(String fileMessage){
      lock.lock();// 上锁
      try{
         System.out.println(Thread.currentThread().getName()+"得到了锁,正在读取文件……");
         for(int i=0; i<fileMessage.length(); i++){
            System.out.print(fileMessage.charAt(i));
         }
         System.out.println();
         System.out.println("文件读取完毕!");
      }finally{
         System.out.println(Thread.currentThread().getName()+"释放了锁!");
         lock.unlock();
      }
   }
   public void demo(final String fileMessage){
      // 创建若干个线程
      ExecutorService service = Executors.newCachedThreadPool();
      // 提交20个任务
      for(int i=0; i<20; i++){
         service.execute(new Runnable() {
            @Override
            public void run() {
               readFile(fileMessage);
               try {
                  Thread.sleep(20);
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
            }
         });
      }
    // 释放线程池中的线程
      service.shutdown();
   }
}
登入後複製

Lock與synchronized的比較

1 、作用

lock 和synchronized 都是Java 中去用來解決執行緒安全問題的一個工具。

2、來源

sychronized 是 Java 中的關鍵字。

lock 是 JUC 套件裡面提供的一個接口,這個接口有很多實現類,其中就包括我們最常用的 ReentrantLock(可重入鎖)。

3、鎖的力度

sychronized 可以用兩種方式去控制鎖的力度:

把 sychronized 關鍵字修飾在方法層面。
修飾在程式碼區塊上。
鎖定對象的不同:

鎖定對象為靜態物件或是class對象,那麼這個鎖屬於全域鎖。
鎖定對象為普通實例對象,那麼這個鎖的範圍取決於這個實例的生命週期。
lock鎖的力度是透過 lock()與unlock()兩個方法決定的。在兩個方法之間的程式碼能保證其線程安全。 lock的作用域取決於lock實例的生命週期。

4、靈活性

lock鎖定比sychronized的彈性更高。

lock可以自主的去決定什麼時候加鎖與釋放鎖。只需要呼叫lock 的lock()和unlock()這兩個方法就可以。

sychronized 由於是關鍵字,所以他無法實作非阻塞競爭鎖的方法,一個執行緒取得鎖之後,其他鎖只能等待那個執行緒釋放之後才能有取得鎖的機會。

5、公平鎖定與非公平鎖定

公平鎖定:多個執行緒依照申請鎖定的順序去拿鎖,執行緒會直接進入佇列去排隊,永遠都是隊列的第一位才能得到鎖。

優點:所有的執行緒都能得到資源,不會餓死。
缺點:吞吐量低,佇列裡面除了第一個線程,其他的線程都會阻塞,cpu喚醒阻塞線程的開銷大。
非公平鎖:多個執行緒去獲取鎖的時候,會直接去嘗試獲取,獲取不到,再去進入等待隊列,如果能獲取到,就直接獲取到鎖。

優點:可以減少CPU喚醒線程的開銷,整體的吞吐效率會高點,CPU也不必取喚醒所有線程,會減少喚起線程的數量。
缺點:可能導致佇列中間的執行緒一直取得不到鎖定或長時間取得不到鎖,最終餓死。
lock提供了公平鎖和非公平鎖兩種機制(預設為非公平鎖)。

sychronized是非公平鎖。

6、異常是否釋放鎖定

synchronized鎖定的釋放是被動的,當sychronized同步程式碼區塊執行結束或出現異常的時候才會被釋放。

lock鎖定發生異常的時候,不會主動釋放佔有的鎖,必須手動unlock()來釋放,所以我們通常都是將同步程式碼區塊放進try-catch裡面,finally中寫入unlock ()方法,避免死鎖發生。

7、判斷是否能取得鎖定

synchronized不能。

lock提供了非阻塞競爭鎖的方法trylock(),回傳值是Boolean型別。它表示的是用來嘗試取得鎖:成功取得則回傳true;取得失敗則回傳false,這個方法無論如何都會立即回傳。

8、調度方式

synchronized使用的是object物件本身的wait、notify、notifyAll方法,而lock使用的是Condition進行執行緒之間的調度。

9、是否能中斷

synchronized只能等待鎖的釋放,不能回應中斷。

lock等待鎖定過程中可以用interrupt()來中斷。

10、效能

如果競爭不激烈,效能差不多;競爭激烈時,lock的效能會更好。

lock鎖定還能使用readwritelock實現讀寫分離,並提高多執行緒的讀取操作效率。

11、sychronized鎖定升級

synchronized 程式碼區塊是由一對 monitorenter/monitorexit 指令實現的。 Monitor的實作完全是依賴作業系統內部的互斥鎖,因為需要進行使用者態到核心態的切換,所以同步操作是無差別的重量級運算。

所以現在JVM提供了三種不同的鎖:偏向鎖、輕量級鎖、重量級鎖。

偏向鎖定:
當沒有競爭出現時,預設使用偏向鎖定。線程會利用 CAS 操作在物件頭上設定線程 ID ,以表示物件偏向當前線程。

目的:在許多應用場景中,大部分物件生命週期最多會被一個執行緒鎖定,使用偏向鎖定可以降低無競爭時的開銷。

輕量級鎖定:
JVM比較目前執行緒的threadID 和Java 物件頭中的threadID是否一致,如果不一致(例如執行緒2要競爭鎖定物件),那麼需要查看Java 物件頭中記錄的線程1是否存活(偏向鎖不會主動釋放因此還是存儲的線程1的threadID),如果沒有存活,那麼鎖對象還是為偏向鎖(對象頭中的threadID為線程2的);如果存活,那麼撤銷偏向鎖,升級為輕量級鎖。

當有其他執行緒想存取加了輕量級鎖的資源時,會使用自旋鎖優化,來進行資源存取。

目的:競爭鎖定物件的執行緒不多,而且執行緒持有鎖的時間也不長的情境。因為阻塞線程需要CPU從用戶態轉到內核態,開銷大,如果剛剛阻塞不久這個鎖就被釋放了,就得不償失了,因此這個時候就乾脆不阻塞這個線程,讓它自旋這等待鎖釋放。

重量級鎖:
自旋失敗,很大機率 再一次自選也是失敗,因此直接升級成重量級鎖,進行執行緒阻塞,減少cpu消耗。

當鎖升級為重量級鎖定後,未搶到鎖的執行緒都會被阻塞,進入阻塞佇列。

以上是Java多執行緒中Lock怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java 中的完美數 Java 中的完美數 Aug 30, 2024 pm 04:28 PM

Java 完美數指南。這裡我們討論定義,如何在 Java 中檢查完美數?

Java 中的隨機數產生器 Java 中的隨機數產生器 Aug 30, 2024 pm 04:27 PM

Java 隨機數產生器指南。在這裡,我們透過範例討論 Java 中的函數,並透過範例討論兩個不同的生成器。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。這裡我們透過範例討論簡介、如何使用 weka java、平台類型和優點。

Java 中的史密斯數 Java 中的史密斯數 Aug 30, 2024 pm 04:28 PM

Java 史密斯數指南。這裡我們討論定義,如何在Java中檢查史密斯號?帶有程式碼實現的範例。

Java Spring 面試題 Java Spring 面試題 Aug 30, 2024 pm 04:29 PM

在本文中,我們保留了最常被問到的 Java Spring 面試問題及其詳細答案。這樣你就可以順利通過面試。

突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

Java 中的時間戳至今 Java 中的時間戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的時間戳記到日期指南。這裡我們也結合範例討論了介紹以及如何在java中將時間戳記轉換為日期。

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

See all articles