日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java多线程:线程间通信之Lock

發(fā)布時(shí)間:2025/3/15 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java多线程:线程间通信之Lock 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Java 5 之后,Java在內(nèi)置關(guān)鍵字sychronized的基礎(chǔ)上又增加了一個(gè)新的處理鎖的方式,Lock類(lèi)。

由于在Java線程間通信:volatile與sychronized中,我們已經(jīng)詳細(xì)的了解了synchronized,所以我們現(xiàn)在主要介紹一下Lock,以及將Lock與synchronized進(jìn)行一下對(duì)比。

1. synchronized的缺陷

synchronized修飾的代碼只有獲取鎖的線程才能夠執(zhí)行,其他線程只能等待該線程釋放鎖。一個(gè)線程釋放鎖的情況有以下方式:

  • 獲取鎖的線程完成了synchronized修飾的代碼塊的執(zhí)行。
  • 線程執(zhí)行時(shí)發(fā)生異常,JVM自動(dòng)釋放鎖。

我們?cè)贘ava多線程的生命周期,實(shí)現(xiàn)與調(diào)度中談過(guò),鎖會(huì)因?yàn)榈却齀/O,sleep()方法等原因被阻塞而不釋放鎖,此時(shí)如果線程還處于用synchronized修飾的代碼區(qū)域里,那么其他線程只能等待,這樣就影響了效率。因此Java提供了Lock來(lái)實(shí)現(xiàn)另一個(gè)機(jī)制,即不讓線程無(wú)限期的等待下去。

思考一個(gè)情景,當(dāng)多線程讀寫(xiě)文件時(shí),讀操作和寫(xiě)操作會(huì)發(fā)生沖突,寫(xiě)操作和寫(xiě)操作會(huì)發(fā)生沖突,但讀操作和讀操作不會(huì)有沖突。如果使用synchronized來(lái)修飾的話,就很可能造成多個(gè)讀操作無(wú)法同時(shí)進(jìn)行的可能(如果只用synchronized修飾寫(xiě)方法,那么可能造成讀寫(xiě)沖突,如果同時(shí)修飾了讀寫(xiě)方法,則會(huì)有讀讀干擾)。此時(shí)就需要用到Lock,換言之Lock比synchronized提供了更多的功能。

使用Lock需要注意以下兩點(diǎn):

  • Lock不是語(yǔ)言內(nèi)置的,synchronized是Java關(guān)鍵字,為內(nèi)置特性,Lock是一個(gè)類(lèi),通過(guò)這個(gè)類(lèi)可以實(shí)現(xiàn)同步訪問(wèn)。
  • 采用synchronized時(shí)我們不需要手動(dòng)去控制加鎖和釋放,系統(tǒng)會(huì)自動(dòng)控制。而使用Lock類(lèi),我們需要手動(dòng)的加鎖和釋放,不主動(dòng)釋放可能會(huì)造成死鎖。實(shí)際上Lock類(lèi)的使用某種意義上講要比synchronized更加直觀。

2. Lock類(lèi)接口設(shè)計(jì)

Lock類(lèi)本身是一個(gè)接口,其方法如下:

public interface Lock {void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition(); }

下面依次講解一下其中各個(gè)方法。

  • lock() 方法使用最多,作用是用于獲取鎖,如果鎖已經(jīng)被其他線程獲得,則等待。
    通常情況下,lock使用以下方式去獲取鎖:
Lock lock = ...; lock.lock(); try{//處理任務(wù) }catch(Exception ex){}finally{lock.unlock(); //釋放鎖 }
  • lockInterruptibly() 和lock()的區(qū)別是lockInterruptibly()鎖定的線程處于等待狀態(tài)時(shí),允許線程的打斷操作,線程使用Thread.interrupt()打斷該線程后會(huì)直接返回并拋出一個(gè)InterruptException();lock()方法鎖定對(duì)象時(shí)如果在等待時(shí)檢測(cè)到線程使用Thread.interrupt(),仍然會(huì)繼續(xù)嘗試獲取鎖,失敗則繼續(xù)休眠,只是在成功獲取鎖之后在把當(dāng)前線程置為interrupt狀態(tài)。也就使說(shuō),當(dāng)兩個(gè)線程同時(shí)通過(guò)lockInterruptibly()想獲取某個(gè)鎖時(shí),假若此時(shí)線程A獲取到了鎖,而線程B只有在等待,那么對(duì)線程B調(diào)用threadB.interrupt()方法能夠中斷線程B的等待過(guò)程。
    因此,lockInterruptibly()方法必須實(shí)現(xiàn)catch(InterruptException e)代碼塊。常見(jiàn)使用方式如下:
public void method() throws InterruptedException {lock.lockInterruptibly();try { //.....}finally {lock.unlock();} }
  • tryLock() 和lock()最大的不同是具有返回值,或者說(shuō),它不去等待鎖。如果它成功獲取鎖,那么返回true;如果它無(wú)法成功獲取鎖,則返回false。
    通常情況下,tryLock使用方式如下:
Lock lock = ...; if(lock.tryLock()) {try{//處理任務(wù)}catch(Exception ex){}finally{lock.unlock(); //釋放鎖} }else {//如果不能獲取鎖,則直接做其他事情 }
  • tryLock(long time, TimeUnit unit) 則是介于二者之間,用戶設(shè)定一個(gè)等待時(shí)間,如果在這個(gè)時(shí)間內(nèi)獲取到了鎖,則返回true,否則返回false結(jié)束。
  • unlock() 從上面的代碼里我們也看到,unlock()一般放在異常處理操作的finally字符控制的代碼塊中。我們要記得Lock和sychronized的區(qū)別,防止產(chǎn)生死鎖。
  • newCondition() 該方法我們放到后面講。

3. ReentrantLock可重入鎖

3.1. ReentrantLock概述

ReentrantLock譯為“可重入鎖”,我們?cè)贘ava多線程:synchronized的可重入性中已經(jīng)明白了什么是可重入以及理解了synchronized的可重入性。ReentrantLock是唯一實(shí)現(xiàn)Lock接口的類(lèi)。

3.2. ReentrantLock使用

考慮到以下情景,一個(gè)僅出售雙人票的演唱會(huì)進(jìn)行門(mén)票出售,有三個(gè)售票口同時(shí)進(jìn)行售票,買(mǎi)票需要100ms時(shí)間,每張票出票需要100ms時(shí)間。該如何設(shè)計(jì)這個(gè)情景?

package com.cielo.LockTest;import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;import static java.lang.Thread.sleep;/*** Created by 63289 on 2017/4/10.*/ class SoldTicket implements Runnable {Lock lock = new ReentrantLock();//使用可重入鎖private volatile Integer ticket;//保證從主內(nèi)存獲取SoldTicket(Integer ticket) {this.ticket = ticket;//提供票數(shù)}private void sold() {lock.lock();//鎖定操作放在try代碼塊外try {if (ticket <= 0) return;//當(dāng)ticket==2時(shí)可能有多個(gè)線程進(jìn)入sold方法,一個(gè)線程運(yùn)行后另外兩個(gè)線程需要退出。sleep(200);//買(mǎi)票0.1s,出票0.1s--ticket;System.out.println("The first ticket is sold by "+Thread.currentThread().getId()+", "+ticket+" tickets leave.");//獲取線程id來(lái)識(shí)別出票站。sleep(100);//出票0.1s--ticket;System.out.println("The second ticket is sold by "+Thread.currentThread().getId()+", "+ticket+" tickets leave.");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}@Overridepublic void run() {while (ticket > 0) {sold();}} }public class LockTest {public static void main(String[] args) {SoldTicket soldTicket = new SoldTicket(20);new Thread(soldTicket).start();new Thread(soldTicket).start();new Thread(soldTicket).start();} }

上面這段代碼結(jié)果如下:

The first ticket is sold by 11, 19 tickets leave. The second ticket is sold by 11, 18 tickets leave. The first ticket is sold by 13, 17 tickets leave. The second ticket is sold by 13, 16 tickets leave. The first ticket is sold by 13, 15 tickets leave. The second ticket is sold by 13, 14 tickets leave. The first ticket is sold by 12, 13 tickets leave. The second ticket is sold by 12, 12 tickets leave. The first ticket is sold by 11, 11 tickets leave. The second ticket is sold by 11, 10 tickets leave. The first ticket is sold by 11, 9 tickets leave. The second ticket is sold by 11, 8 tickets leave. The first ticket is sold by 13, 7 tickets leave. The second ticket is sold by 13, 6 tickets leave. The first ticket is sold by 13, 5 tickets leave. The second ticket is sold by 13, 4 tickets leave. The first ticket is sold by 13, 3 tickets leave. The second ticket is sold by 13, 2 tickets leave. The first ticket is sold by 13, 1 tickets leave. The second ticket is sold by 13, 0 tickets leave.

如果我們不對(duì)售票操作進(jìn)行鎖定,則會(huì)有以下幾個(gè)問(wèn)題:

  • 出售第一張票后其他機(jī)器出了另一張票,導(dǎo)致票沒(méi)有成對(duì)賣(mài)。
  • 已經(jīng)無(wú)票后仍有機(jī)器出票造成混亂。

顯然,本題的情景用synchronized也可以很容易的實(shí)現(xiàn),實(shí)際上Lock有別于synchronized的主要點(diǎn)是lockInterruptibly()和tryLock()這兩個(gè)可以對(duì)鎖進(jìn)行控制的方法。

4. ReadWriteLock讀寫(xiě)鎖

4.1. ReadWriteLock接口

回到開(kāi)頭synchronized缺陷的介紹,實(shí)際上,Lock接口的重要衍生接口ReadWriteLock即是解決這一問(wèn)題。ReadWriteLock定義很簡(jiǎn)單,僅有兩個(gè)接口:

public interface ReadWriteLock {/*** Returns the lock used for reading.** @return the lock used for reading.*/Lock readLock();/*** Returns the lock used for writing.** @return the lock used for writing.*/Lock writeLock(); }

即是它只提供了readLock()和writeLock()兩個(gè)操作,這兩個(gè)操作均返回一個(gè)Lock類(lèi)的實(shí)例。兩個(gè)操作一個(gè)獲取讀鎖,一個(gè)獲取寫(xiě)鎖,將讀寫(xiě)分開(kāi)進(jìn)行操作。ReadWriteLock將讀寫(xiě)的鎖分開(kāi),可以讓多個(gè)讀操作并行,這就大大提高了效率。使用ReadWriteLock時(shí),用讀鎖去控制讀操作,寫(xiě)鎖控制寫(xiě)操作,進(jìn)而實(shí)現(xiàn)了一個(gè)可以在如下的大量讀少量寫(xiě)且讀者優(yōu)先的情景運(yùn)行的鎖。

4.2. ReentrantReadWriteLock可重入讀寫(xiě)鎖

ReentrantReadWriteLock是ReadWriteLock的唯一實(shí)例。同時(shí)提供了很多操作方法。ReentratReadWriteLock接口實(shí)現(xiàn)的讀鎖寫(xiě)鎖進(jìn)入有如下要求:

4.2.1. 線程進(jìn)入讀鎖的要求

  • 沒(méi)有其他線程的寫(xiě)鎖。
  • 沒(méi)有鎖請(qǐng)求 或 調(diào)用寫(xiě)請(qǐng)求的線程正是該線程。

4.2.2. 線程進(jìn)入寫(xiě)鎖的要求

  • 沒(méi)有其他線程的讀鎖。
  • 沒(méi)有其他線程的寫(xiě)鎖。

4.2.3. 讀寫(xiě)鎖使用示例

private SomeClass someClass;//資源 private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();//創(chuàng)建鎖 private final Lock readLock = readWriteLock.readLock();//讀鎖 private final Lock writeLock = readWriteLock.writeLock();//寫(xiě)鎖 //讀方法 readLock.lock(); try {result = someClass.someMethod(); } catch (Exception e) {e.printStackTrace(); } finally {readLock.unlock(); } //寫(xiě)方法,產(chǎn)生新的SomeClass實(shí)例tempSomeClass writeLock.lock(); try{this.someClass = tempSomeClass;//更新 }catch (Exception e) {e.printStackTrace(); } finally{writeLock.unlock(); }

5. 公平鎖

公平鎖即當(dāng)多個(gè)線程等待的一個(gè)資源的鎖釋放時(shí),線程不是隨機(jī)的獲取資源而是等待時(shí)間最久的線程獲取資源(FIFO)。Java中,synchronized是一個(gè)非公平鎖,無(wú)法保證鎖的獲取順序。ReentrantLock和ReentrantReadWriteLock默認(rèn)也是非公平鎖,但可以設(shè)置成公平鎖。我們前面的實(shí)例中初始化ReentrantLock和ReentrantReadWriteLock時(shí)都是無(wú)參數(shù)的。實(shí)際上,它們提供一個(gè)默認(rèn)的boolean變量fair,為true則為公平鎖,為false則為非公平鎖,默認(rèn)為false。因此,當(dāng)我們想將其實(shí)現(xiàn)為公平鎖時(shí),僅需要初始化時(shí)賦值true。即:

Lock lock = new ReentrantLock(true);

考慮前面賣(mài)票的實(shí)例,如果改為公平鎖(盡管這和情景無(wú)關(guān)),則結(jié)果輸出非常整齊如下:

The first ticket is sold by 11, 19 tickets leave. The second ticket is sold by 11, 18 tickets leave. The first ticket is sold by 12, 17 tickets leave. The second ticket is sold by 12, 16 tickets leave. The first ticket is sold by 13, 15 tickets leave. The second ticket is sold by 13, 14 tickets leave. The first ticket is sold by 11, 13 tickets leave. The second ticket is sold by 11, 12 tickets leave. The first ticket is sold by 12, 11 tickets leave. The second ticket is sold by 12, 10 tickets leave. The first ticket is sold by 13, 9 tickets leave. The second ticket is sold by 13, 8 tickets leave. The first ticket is sold by 11, 7 tickets leave. The second ticket is sold by 11, 6 tickets leave. The first ticket is sold by 12, 5 tickets leave. The second ticket is sold by 12, 4 tickets leave. The first ticket is sold by 13, 3 tickets leave. The second ticket is sold by 13, 2 tickets leave. The first ticket is sold by 11, 1 tickets leave. The second ticket is sold by 11, 0 tickets leave.

6. Lock和synchronized的選擇

  • synchronized是內(nèi)置語(yǔ)言實(shí)現(xiàn)的關(guān)鍵字,Lock是為了實(shí)現(xiàn)更高級(jí)鎖功能而提供的接口。
  • Lock實(shí)現(xiàn)了tryLock等接口,線程可以不用一直等待。
  • synchronized發(fā)生異常時(shí)自動(dòng)釋放占有的鎖,Lock需要在finally塊中手動(dòng)釋放鎖。因此從安全性角度講,既可以用Lock又可以用synchronized時(shí)(即不需要鎖的更高級(jí)功能時(shí))使用synchronized更保險(xiǎn)。
  • Lock可以通過(guò)lockInterruptibly()接口實(shí)現(xiàn)可中斷鎖。
  • 由于Lock提供了時(shí)間限制同步,可被打斷同步等機(jī)制,線程激烈競(jìng)爭(zhēng)時(shí)Lock的性能遠(yuǎn)優(yōu)于synchronized,即有大量線程時(shí)推薦使用Lock。在競(jìng)爭(zhēng)不激烈時(shí),由于synchronized的編譯器優(yōu)化更好,性能更佳。
  • ReentrantReadWriteLock實(shí)現(xiàn)了封裝好的讀寫(xiě)鎖用于大量讀少量寫(xiě)讀者優(yōu)先情景解決了synchronized讀寫(xiě)情景難以實(shí)現(xiàn)問(wèn)題。

7. 參考文章

Java并發(fā)編程:Lock

lock和lockInterruptibly

說(shuō)說(shuō)ReentrantReadWriteLock

轉(zhuǎn)載于:https://www.cnblogs.com/cielosun/p/6662201.html

總結(jié)

以上是生活随笔為你收集整理的Java多线程:线程间通信之Lock的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。