Spring IOC容器併發條件下,可能發生死鎖

2021-08-31 17:09:33 字數 3088 閱讀 1287

1.背景

上週在生產環境應用啟動時,發生應用頻頻發生死鎖的現象。原因是因為 spring ioc 容器還未初始化完成,就有工作執行緒呼叫 context.getbean() 來獲取容器裡的物件。具體產生死鎖的原因條件有:

1.       應用啟動的時候 main 執行緒進行 spring 容器初始化。

2.       容器初始化的過程中有工作執行緒也起來了並開始工作。

3.       工作執行緒**裡顯式呼叫 spring ioc 容器的 context.getbean(string beanname) 。

4.       工作執行緒顯式獲取的 bean 未例項化,且裡存在直接或者間接的註解注入方式的情況。

以上情況都符合,那工作執行緒和 main 執行緒可能發生死鎖。

2.具體原因分析

spring ioc 容器組合裡有兩個重要的 map :

/** map of bean definition objects, keyed by bean name */

privatefinalmap

beandefinitionmap

= collectionfactory.createconcurrentmapifpossible (16);

//bean definition 是 spring 容器裡描述 bean 物件的元資料( bean 的建立等就是基於此來建立)。 spring 容器初始化例項之前需要先把配置檔案的 bean 定義都轉化成內部的統一描述物件 beandefinition 。該 beandefinitionmap 用於儲存這些資料。

/** cache of singleton objects: bean name

-->

bean instance */

privatefinalmap

singletonobjects

= collectionfactory.createconcurrentmapifpossible (16);

//spring 容器用於 cache 住 spring 容器初始化的單例物件

以上兩個物件為了保證資料的一致性,在操作的時候很多時候會進行加鎖。如以下兩個過程。

過程一: spring容器初始化

spring 容器初始化的時候會例項化所有單例物件( preinstantiatesingletons ),這個過程中會對上面兩個物件加鎖,以防止併發。先對 beandefinitionmap 加鎖,防止元資料被修改,然後在每次例項化單例物件的時候對 singletonobjects 加鎖,防止併發修改。

過程二:根據 spring容器獲取乙個單例物件。

呼叫 spring 容器的 context.getbean ( beanname ),如果該 bean 是單例且還未例項化,這個時候就需要進行例項化,如果該 bean 直接或間接存在註解方式的 bean 注入的時候,過程中也會對以上兩個物件進行加鎖防止併發。先對 singleobjects 加鎖,從改 map 裡找是否有存在 beanname 的物件,沒有的話在建立該 bean 的過程中會對 beandefinitionmap 加鎖。

可以看出以上過程一和過程二對兩個物件的鎖順序是不一致的,所以併發執行就可能會發生死鎖。

在本機寫了乙個簡單的實驗,死鎖的執行緒棧資訊可以證明這一點,具體如下:

**十分簡單,如下:

package com.alibaba.test;

public class deadlocktest

public void getbean()

/*** @param args

*/public static void main(string args)

//get bean 工作執行緒

class getbean implements runnable catch (interruptedexception e)

// todo auto-generated method stub}}

}

其中mybean定義如下(一定要用註解的注入方式,才會有可能發生死鎖!):

package com.alibaba.test;

import org.springframework.beans.factory.annotation.autowired;

public class mybean

public void setotherbean(otherbean otherbean)

public void sayhello()

}

mybean.xml:

<?xml version="1.0" encoding="gb2312"?>

以上**經過除錯控制,即會發生思索,控制如下:

1.main執行緒在defaultlistablebeanfactory.preinstantiatesingletons 方法的

synchronized (this.beandefinitionmap) 裡加個斷點

2.getbean工作執行緒在defaultsingletonbeanregistry.getsingleton(string beanname, objectfactory singletonfactory)方法的

synchronized (this.singletonobjects) 裡加個斷點。

3.等1,2斷點都進去之後,再觸發繼續執行,就會發生死鎖。

結論也許可以算是 spring 的 bug ,也許可以算我們使用不當。

總之,有兩點需要注意:

1.       盡量避免顯式呼叫 ioc 容器,注入工作由容器自己來完成。

2.       盡量在容器初始化完,開始對外服務。

Minor GC ,Full GC 觸發條件

首先澄清一點,關於full gc和major gc,個人理解是同乙個東西。minor gc觸發條件 當eden區滿時,觸發minor gc。full gc觸發條件 1 呼叫system.gc時,系統建議執行full gc,但是不必然執行 2 老年代空間不足 3 方法區空間不足 4 通過minor g...

Minor GC ,Full GC 觸發條件

minor gc full gc 觸發條件 minor gc觸發條件 當eden區滿時,觸發minor gc。full gc觸發條件 1 呼叫system.gc時,系統建議執行full gc,但是不必然執行 2 老年代空間不足 3 方法去空間不足 4 通過minor gc後進入老年代的平均大小大於老...

高併發條件下的快取穿透問題

1 快取穿透 快取穿透是指查詢乙個一定不存在的資料,由於快取是不命中時被動寫的,並且出於容錯考慮,如果從儲存層查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到儲存層去查詢,失去了快取的意義。在流量大時,可能db就掛掉了,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞。key不...