溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java實(shí)現(xiàn)多線程死鎖與資源限制

發(fā)布時(shí)間:2020-10-29 16:47:58 來(lái)源:億速云 閱讀:158 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

本篇文章給大家分享的是有關(guān)Java實(shí)現(xiàn)多線程死鎖與資源限制,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

鎖是個(gè)非常有用的工具,運(yùn)用場(chǎng)景非常多,因?yàn)樗褂闷饋?lái)非常簡(jiǎn)單,而且易于理解。但同時(shí)它也會(huì)帶來(lái)一些困擾,那就是可能會(huì)引起死鎖,一旦產(chǎn)生死鎖,就會(huì)造成系統(tǒng)功能不可用。

死鎖的概念

那什么是死鎖呢?所謂死鎖: 是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過(guò)程中,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程。

死鎖產(chǎn)生的必要條件

1)互斥條件:指進(jìn)程對(duì)所分配到的資源進(jìn)行排它性使用,即在一段時(shí)間內(nèi)某資源只由一個(gè)進(jìn)程占用。如果此時(shí)還有其它進(jìn)程請(qǐng)求資源,則請(qǐng)求者只能等待,直至占有資源的進(jìn)程用畢釋放。

2)請(qǐng)求和保持條件:指進(jìn)程已經(jīng)保持至少一個(gè)資源,但又提出了新的資源請(qǐng)求,而該資源已被其它進(jìn)程占有,此時(shí)請(qǐng)求進(jìn)程阻塞,但又對(duì)自己已獲得的其它資源保持不放。

3)不剝奪條件:指進(jìn)程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時(shí)由自己釋放。

4)環(huán)路等待條件:指在發(fā)生死鎖時(shí),必然存在一個(gè)進(jìn)程——資源的環(huán)形鏈,即進(jìn)程集合{P0,P1,P2,···,Pn}中的P0正在等待一個(gè)P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。

死鎖代碼實(shí)例

public class DeadLockDemo {
  private static String A = "A";
  private static String B = "B";
  public static void main(String[] args) {
    new DeadLockDemo().deadLock();
  }
  /**
   * 死鎖
   * @author fuyuwei
   * 2017年5月13日 下午9:27:32
   */
  private void deadLock() {
    Thread t1 = new Thread(new Runnable() {
      @SuppressWarnings("static-access")
      @Override
      public void run() {
        synchronized (A) {
          try {
            Thread.currentThread().sleep(2000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          synchronized (B) {
            System.out.println("1");
          }
        }
      }
    });
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        synchronized (B) {
          synchronized (A) {
            System.out.println("2");
          }
        }
      }
    });
    t1.start();
    t2.start();
  }
}

線程A睡眠2秒之后鎖定B同步打印1,但是這時(shí)候B已經(jīng)被第二個(gè)線程鎖定,并且第二天線程又鎖定A打印2,就這樣A等待B但是握著B(niǎo)不放,B等待A但是握著A不放,就產(chǎn)生了死鎖。

當(dāng)然這段代碼純粹是為了演示死鎖,在實(shí)際工作中基本上不會(huì)出現(xiàn)這種代碼。在實(shí)際工作中線程可能拿到一個(gè)數(shù)據(jù)庫(kù)鎖,釋放鎖的時(shí)候拋出了異常,沒(méi)釋放掉。

一旦出現(xiàn)死鎖,業(yè)務(wù)是可感知的,因?yàn)椴荒芾^續(xù)提供服務(wù)了,那么只能通過(guò)dump線程查看到底是哪個(gè)線程出現(xiàn)了問(wèn)題,以下線程信息告訴我們是DeadLockDemo類(lèi)的第42行和第31行引起的死鎖。

"Thread-2" prio=5 tid=7fc0458d1000 nid=0x116c1c000 waiting for monitor entry [116c1b000
java.lang.Thread.State: BLOCKED (on object monitor)
at com.ifeve.book.forkjoin.DeadLockDemo$2.run(DeadLockDemo.java:42)
- waiting to lock <7fb2f3ec0> (a java.lang.String)
- locked <7fb2f3ef8> (a java.lang.String)
at java.lang.Thread.run(Thread.java:695)
"Thread-1" prio=5 tid=7fc0430f6800 nid=0x116b19000 waiting for monitor entry [116b18000
java.lang.Thread.State: BLOCKED (on object monitor)
at com.ifeve.book.forkjoin.DeadLockDemo$1.run(DeadLockDemo.java:31)
- waiting to lock <7fb2f3ef8> (a java.lang.String)
- locked <7fb2f3ec0> (a java.lang.String)
at java.lang.Thread.run(Thread.j

避免死鎖的方法

1、避免一個(gè)線程同時(shí)獲取多個(gè)鎖。

2、避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源。

3、嘗試使用定時(shí)鎖,使用lock.tryLock(timeout)來(lái)替代使用內(nèi)部鎖機(jī)制。

4、對(duì)于數(shù)據(jù)庫(kù)鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫(kù)連接里,否則會(huì)出現(xiàn)解鎖失敗的情況。

什么是資源限制

資源限制是指在進(jìn)行并發(fā)編程時(shí),程序的執(zhí)行速度受限于計(jì)算機(jī)硬件資源或軟件資源。例如,服務(wù)器的帶寬只有2Mb/s,某個(gè)資源的下載速度是1Mb/s每秒,系統(tǒng)啟動(dòng)10個(gè)線程下載資源,下載速度不會(huì)變成10Mb/s,所以在進(jìn)行并發(fā)編程時(shí),要考慮這些資源的限制。硬件資源限制有帶寬的上傳/下載速度、硬盤(pán)讀寫(xiě)速度和CPU的處理速度。軟件資源限制有數(shù)據(jù)庫(kù)的連接數(shù)和socket連接數(shù)等。

資源限制引發(fā)的問(wèn)題

在并發(fā)編程中,將代碼執(zhí)行速度加快的原則是將代碼中串行執(zhí)行的部分變成并發(fā)執(zhí)行,但是如果將某段串行的代碼并發(fā)執(zhí)行,因?yàn)槭芟抻谫Y源,仍然在串行執(zhí)行,這時(shí)候程序不僅不會(huì)加快執(zhí)行,反而會(huì)更慢,因?yàn)樵黾恿松舷挛那袚Q和資源調(diào)度的時(shí)間。例如,之前看到一段程序使用多線程在辦公網(wǎng)并發(fā)地下載和處理數(shù)據(jù)時(shí),導(dǎo)致CPU利用率達(dá)到100%,幾個(gè)小時(shí)都不能運(yùn)行完成任務(wù),后來(lái)修改成單線程,一個(gè)小時(shí)就執(zhí)行完成了。

如何解決資源限制的問(wèn)題

對(duì)于硬件資源限制,可以考慮使用集群并行執(zhí)行程序。既然單機(jī)的資源有限制,那么就讓程序在多機(jī)上運(yùn)行。比如使用ODPS、Hadoop或者自己搭建服務(wù)器集群,不同的機(jī)器處理不同的數(shù)據(jù)。可以通過(guò)“數(shù)據(jù)ID%機(jī)器數(shù)”,計(jì)算得到一個(gè)機(jī)器編號(hào),然后由對(duì)應(yīng)編號(hào)的機(jī)器處理這筆數(shù)據(jù)。對(duì)于軟件資源限制,可以考慮使用資源池將資源復(fù)用。比如使用連接池將數(shù)據(jù)庫(kù)和Socket連接復(fù)用,或者在調(diào)用對(duì)方webservice接口獲取數(shù)據(jù)時(shí),只建立一個(gè)連接。

在資源限制情況下進(jìn)行并發(fā)編程

如何在資源限制的情況下,讓程序執(zhí)行得更快呢?方法就是,根據(jù)不同的資源限制調(diào)整程序的并發(fā)度,比如下載文件程序依賴于兩個(gè)資源——帶寬和硬盤(pán)讀寫(xiě)速度。有數(shù)據(jù)庫(kù)操作時(shí),涉及數(shù)據(jù)庫(kù)連接數(shù),如果SQL語(yǔ)句執(zhí)行非??欤€程的數(shù)量比數(shù)據(jù)庫(kù)連接數(shù)大很多,則某些線程會(huì)被阻塞,等待數(shù)據(jù)庫(kù)連接。

補(bǔ)充知識(shí):初入Java并發(fā)-避免死鎖的常見(jiàn)方法

1、避免一個(gè)線程同時(shí)獲取多個(gè)鎖

2、避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源

3、嘗試使用定時(shí)鎖,使用lock.tryLock(timeout)來(lái)替代使用內(nèi)部鎖機(jī)制

4、對(duì)于數(shù)據(jù)庫(kù)鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫(kù)連接里,否則會(huì)出現(xiàn)解鎖失敗的情況。

以上就是Java實(shí)現(xiàn)多線程死鎖與資源限制,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI