一個線程執行synchronized同步代碼時,再次重入該鎖過程中,如果拋出異常,會釋放鎖嗎?
如果鎖的計數器為1,拋出異常,會直接釋放鎖;
那如果鎖的計數器為2,拋出異常,會直接釋放鎖嗎?
來簡單測試一下
@Slf4j public class SynchronizedExceptionRunnable implements Runnable { private volatile boolean flag = true; @Override public void run() { synchronized (this) { if (flag) { //讓先啟動的線程先執行異常方法methodB后,flag==false,並且拋出異常線程停止,直接釋放鎖,不會執行后面的代碼; methodB(); } else { //后啟動的線程再獲取鎖,進入if-else,再獲取鎖執行methodA methodA(); } log.info("{}:if-else end!",Thread.currentThread().getName()); } } public synchronized void methodA(){ log.info("ThreadName:{}----methodA", Thread.currentThread().getName()); } public synchronized void methodB() { flag = false; log.warn("ThreadName:{}----methodB will throw a exception!",Thread.currentThread().getName()); //如果把下面這行拋異常的代碼注釋掉,會執行下面的線程睡眠5秒和最后的日志代碼 //如果不注釋,會拋出異常,不在執行后面的代碼,並且釋放鎖,methodA方法就會執行 int a = 1/0; try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } log.info("ThreadName:{}----methodB End!",Thread.currentThread().getName()); } }
啟動類
public class Main { public static void main(String[] args) { SynchronizedExceptionRunnable runnable = new SynchronizedExceptionRunnable(); Thread thread1 = new Thread(runnable,"杯子"); Thread thread2 = new Thread(runnable,"人"); thread1.start(); thread2.start(); } }
執行結果如下圖:
結果分析:
當“杯子”線程獲取到鎖,鎖的計數器為1,因為哨兵flag的原因,先獲取到鎖的線程調用方法methodB,會再次獲取鎖(因為synchronized是可重入鎖),此時鎖的計數器為2,然后執行methodB,該方法會拋出異常,鎖的計數器直接置為0,直接釋放鎖;
然后“人”線程獲取到鎖,鎖的計數器為1,由於flag在methodB中被設置為false,調用沒有異常的methodA,會再次獲取鎖,此時鎖的計數器為2,執行完methodA,鎖的計數器-1,此時鎖的計數器為1,再執行完run方法中的if-else,打印日志,最后釋放鎖。
如果不拋異常,是什么情況呢?我們把拋異常的代碼int a = 1/0 注釋掉。執行結果如下:
這個結果大家肯定清楚,就不在贅述。
總結
所以如果鎖的計數器為2,執行過程中拋出異常,鎖的計數器直接置為0,會直接釋放鎖!
應該是一個線程,如果執行同步代碼塊過程中拋出異常未捕獲,會立即終止,退出同步代碼塊,並且釋放鎖,不會執行后續代碼。
最核心的就是拋了異常,線程內部如果沒處理,線程會直接停止!