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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

线程基础知识_Synchronized_ThreadAPI_自定义锁_获取线程运行时异常

發布時間:2024/7/5 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线程基础知识_Synchronized_ThreadAPI_自定义锁_获取线程运行时异常 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Synchronized

synchronized包含monitor enter, monitor exit 2個JVM指令(遵循happens-before原則), 執行monitor exit之前必須存在monitor enter指令.
由于每個對象與一個monitor產生關聯, 線程執行monitor enter時, 就會獲取monitor的lock, monitor中存在計數器, 用于記錄當前lock被獲取的情況, 當線程已經獲取了lock, 再次進行線程重入, lock往上自增.
與之相反, 當線程執行到monitor exit時, 對應的monitor計數器就會自減

  • this monitor: synchronized修飾的對象是this
public synchronized void test(){//... } //A實現Runnable接口 A a = new A(); new Thread(a).start(); new Thread(a).start();
  • class monitor: synchronized修飾的對象是class
public synchronized static void test() {//... } 或者 public void test() {synchronized(A.class) {//...} } //A實現Runnable接口 A a1 = new A(); A a2 = new A(); //此時a1 與a2 共用一個class monitor new Thread(a1).start(); new Thread(a2).start();

Thread API

sleep, yield, wait, notify/notifyAll, interrupt, interrupted, join

  • sleep
    Thread.sleep(), 線程休眠, 但休眠不會釋放鎖資源, 線程從running => block的狀態切換, 可以使用thread.interrupt中斷睡眠
  • yield
    Thread.yield(), 當前線程放棄CPU資源(CPU資源不緊張, 將忽略), 線程從running => runnable的狀態切換, 因此不能使用interrupt中斷處于runnable狀態的線程
  • wait
    線程放棄鎖資源, 并進入與鎖關聯的waitSet集合, 狀態變化: Running => Blocked, 可以設定
    object.wait()等價于object.wait(0), 表示阻塞時間是永遠
    注: wait必須在同步代碼內. waitSet依賴于鎖資源, 沒有在同步代碼內, 拋出IllegalMonitorStateException
    wait與sleep相似點:
1. 都能使線程阻塞 2. 都是可中斷 3. wait屬于Object, sleep屬于Thread 4. wait需要在同步代碼塊中, sleep不需要 5. wait狀態下, 將會是否鎖資源, sleep不會釋放鎖資源 6. sleep執行一段自動退出阻塞狀態, wait(long)一樣.
  • notify/notifyAll
    notify隨機喚醒waitSet集合中某一線程, notifyAll喚醒waitSet集合中所有的線程, 被喚醒的線程重新爭搶鎖資源, 爭搶到之后, 接著剛才wait的地方往后執行
    注: notify必須在同步代碼內. waitSet依賴于鎖資源, 沒有在同步代碼內, 拋出IllegalMonitorStateException
  • interrupt
    打斷當前線程阻塞狀態, 可使用interrupt進行中斷的方法如下:
object.wait(), object(long), Thread.sleep(long), thread.join(), Selector.wakeup() public void interrupt() {if (this != Thread.currentThread())checkAccess();synchronized (blockerLock) {Interruptible b = blocker;if (b != null) {//設置中斷標識, interrupt0屬于native方法interrupt0(); b.interrupt(this);return;}}interrupt0();}

當線程使用休眠方法進入阻塞狀態后, 使用thread.interrupt設置中斷標志, 隨后線程被中斷, 拋出InterruptedException, 線程終止運行
isInterrupt: 用于判斷線程是否被中斷, 從下面源碼看出, 判斷線程是否被中斷, 不會清除中斷標志

public boolean isInterrupted() {//native方法, clearInterrupted設置為falsereturn isInterrupted(false);}

思考: wait, sleep, yield被中斷后會不會清除中斷標志

/*** 測試* @author regotto*/ public class InterruptTest {private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {synchronized (lock) {try {System.out.println("1.isInterrupted: " + Thread.currentThread().isInterrupted());lock.wait();} catch (InterruptedException e) {System.out.println("2.isInterrupted: " + Thread.currentThread().isInterrupted());}}});thread.start();TimeUnit.SECONDS.sleep(1);thread.interrupt();TimeUnit.SECONDS.sleep(1);System.out.println("3.isInterrupted: " + Thread.currentThread().isInterrupted());} }

上述結果都為false, 由于isInterrupt不會清除中斷標志, 因此得出結論, 中斷標志是被wait清除的, 同理sleep, yield也是一樣的.

  • interrupted: 該方法直接看源碼
public static boolean interrupted() {return currentThread().isInterrupted(true);}

從源碼可以看出, 該方法就是調用isInterrupt方法, 但是, 注意傳的參數是true, 意味著, 調用該方法后, 將會清除中斷標志,

  • join
    A join B, B blocked 直到 A 變為terminal狀態
    join源碼如下:
public final synchronized void join(long millis)throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {while (isAlive()) {wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}

根據源碼得出如下結論:
join內部機制就是wait, 一定記住, 調用wait時, 誰調用, 誰進入blocked, 不要搞混了

/*** 測試join,join內部使用wait,使調用join的線程進行wait,當任務線程執行完,JVM底層自動調用notify* @author regotto*/ public class JoinTest {public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);}}, "t1");t1.start();try {//調用join線程是main線程,main線程進入join后處于wait狀態,直到t1線程執行完,才notify//notify的過程由JVM底層自動調用//此時的join是相對于main,main線程是最終的調用者t1.join();} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);}} }

測試join狀態下的interrupt

/*** 測試join狀態下的interrupt* @author regotto*/ public class JoinSateExecuteInterrupt {public static void main(String[] args) {Thread main = Thread.currentThread();Thread thread = new Thread(() -> {while (true) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " interrupt");e.printStackTrace();break;}}});thread.start();//此處提前設置中斷狀態 // main.interrupt();thread.interrupt();try {thread.join();} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " interrupt");e.printStackTrace();} // thread.interrupt(); //此處調用interrupt不能使join方法進入interrupt,因為一直執行thread的run操作,main線程的此行代碼永遠不會執行} }

根據Thread API實現自定義鎖

需求分析: 實現synchronized的功能, 保證當線程處于阻塞狀態可被中斷(此處的阻塞代表線程在等待獲取對象monitor), 線程在阻塞狀態下可以計時, 避免線程資源浪費.
詳細設計: 使用wait, notify, 標志變量設計lock, 模擬synchronized同步過程, A線程進入同步代碼塊, 將標志變量變為true, 此時wait有效, B進入線程進入阻塞狀態, 當A線程執行完畢, 調用notifyAll, 喚醒被阻塞的線程, 在此過程中, 進入阻塞狀態下的線程可被中斷.

抽象接口定義:

package com.concurrent.definelock;import java.util.List; import java.util.concurrent.TimeoutException;public interface Lock {void lock() throws InterruptedException;void lock(long mills) throws InterruptedException, TimeoutException;void unlock();/*** 用于獲取處于阻塞狀態的所有線程* @return list<thread>*/List<Thread> getBlockedThreads(); }

實現類:

package com.concurrent.definelock;import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException;public class BooleanLock implements Lock {/*** currentThread: 當前擁有鎖的線程* locked: 鎖是被被獲得,默認false未獲得,當調用lock能準確加鎖* blockedList: 存儲哪些線程進入阻塞狀態*/private Thread currentThread;private boolean locked = false;private final List<Thread> blockedList = new ArrayList<>();/*** 第一個線程進入的時候, 標記為加鎖的線程, 剩下的線程進入之后全都wait* 直到該線程unlock,剩下的線程才重新喚醒* @throws InterruptedException*/@Overridepublic void lock() throws InterruptedException {synchronized (this) {while (locked) {if (!blockedList.contains(currentThread)){blockedList.add(currentThread);}System.out.println(Thread.currentThread().getName() + ": 進入wait狀態");//所有線程被喚醒, 只有拿到鎖的那個線程才能接著下去執行, 剩余鎖都只能處于阻塞狀態//wait可中斷方法, 當捕捉到Interrupt信號的時候, 拋出異常, 線程中斷this.wait();System.out.println(Thread.currentThread().getName() + ": 被喚醒");}blockedList.remove(currentThread);System.out.println(Thread.currentThread().getName() + ": 刪除在blockList中值, 設置locked為true");this.locked = true;this.currentThread = Thread.currentThread();}System.out.println(Thread.currentThread().getName() + ": 結束lock方法, 已出synchronize模塊");}/*** 傳入mills <=0 立刻加鎖也可嘗試拋異常, 否則經過mills再加鎖* @param mills* @throws InterruptedException* @throws TimeoutException*/@Overridepublic void lock(long mills) throws InterruptedException, TimeoutException {synchronized (this) {if (0 >= mills) {this.lock();} else {long remainingMills = mills;long endMills = System.currentTimeMillis() + remainingMills;while (locked) {if (0 >= remainingMills){throw new TimeoutException();}if (!blockedList.contains(Thread.currentThread())) {blockedList.add(Thread.currentThread());}this.wait(remainingMills);remainingMills = endMills - System.currentTimeMillis();}blockedList.remove(Thread.currentThread());this.locked = true;this.currentThread = Thread.currentThread();}}}@Overridepublic void unlock() {System.out.println(Thread.currentThread().getName() + ": 進入unlock");synchronized (this) {//不加此步操作,任何線程都將可以執行notifyif (currentThread == Thread.currentThread()) {//修改locked, 表示未加鎖, 喚醒剩余線程System.out.println(Thread.currentThread().getName() + ": 設置locked=false");this.locked = false;this.notifyAll();}}}@Overridepublic List<Thread> getBlockedThreads() {return blockedList;} }

測試類:

package com.concurrent.definelock;import java.util.concurrent.TimeUnit; import java.util.stream.IntStream;/*** 測試自定義BooleanLock* @author regotto*/ public class BooleanLockTest {private final Lock lock = new BooleanLock();public void syncMethod() {try {lock.lock();TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {BooleanLockTest blt = new BooleanLockTest();//定義10個線程, 進行start測試IntStream.range(0, 10).mapToObj(i -> new Thread(blt::syncMethod)).forEach(Thread::start);} }

獲取線程運行時異常

線程執行過程中, 可以為特定線程/全局線程設置運行時異常捕獲, 通常使用Thread.setDefaultUncaughtExceptionHandler進行設置.

package com.concurrent.dealthreadexception;import java.util.concurrent.TimeUnit;public class CaptureThreadException {public static void main(String[] args) {//設置線程回調接口, 使得run方法出現異常, 回調Hook能捕獲(該操作類似于監聽者模式)//如果當前線程組沒有設置回調接口, 那么就去父group中查找, 直到找到, 一直找不到//就執行System.errThread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {System.out.println("回調Hook捕獲" + thread.getName() + "的run拋出的異常: " + throwable.getMessage());});final Thread thread = new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//run方法不能拋出異常, 就算拋出也無法捕獲, 只能使用特定的Hook獲取異常System.out.println(1 / 0);}, "Test0");thread.start();} }

Thread.setDefaultUncaughtExceptionHandler源碼如下:

public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {SecurityManager sm = System.getSecurityManager();if (sm != null) {sm.checkPermission(new RuntimePermission("setDefaultUncaughtExceptionHandler"));}//設置默認的異常捕獲處理器, 0defaultUncaughtExceptionHandler = eh;}

總結

以上是生活随笔為你收集整理的线程基础知识_Synchronized_ThreadAPI_自定义锁_获取线程运行时异常的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。