多線程開(kāi)發(fā)常見(jiàn)問(wèn)題匯總
1. Thread.UncaughtExceptionHandler
UncaughtExceptionHandler? 是一個(gè)接口,用于處理線程因未捕獲異常而突然終止的情況。
雖然,通常都會(huì)在線程執(zhí)行的代碼中加try...catch來(lái)捕獲異常,那么如果某些異常沒(méi)有被catch住(比如,線程突然死掉了)那么我們將不知道發(fā)生了什么。因此,給每個(gè)現(xiàn)在設(shè)置一個(gè)未捕獲異常處理器很有必要。
@Slf4j
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
log.info("線程異常: {}", t.getName(), e);
}
}
public static void main(String[] args) {
// 設(shè)置全局默認(rèn)的未捕獲異常處理器
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
Thread thread = new Thread(() -> {
int a = 1 / 0;
});
// 給某個(gè)線程設(shè)置自己的未捕獲異常處理器
thread.setUncaughtExceptionHandler(((t, e) -> {
System.out.println("線程執(zhí)行異常!線程名稱: " + t.getName());
logger.error("線程執(zhí)行異常!名稱: {}", t.getName(), e);
}));
thread.start();
}
通常我們采用線程池的方式使用線程,下面是在線程池中使用方式
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
return t;
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
int a = 1 / 0;
}
});
}
2. CountDownLatch(倒計(jì)時(shí))
CountDownLatch 是 Java 中的一個(gè)同步工具類,它允許一個(gè)或多個(gè)線程等待其他線程完成操作。
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
return t;
}
});
int count = 10; // 10個(gè)任務(wù)
CountDownLatch latch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
// executorService.execute(()->{
// try {
//
// } catch (Exception ex) {
//
// } finally {
// latch.countDown();
// }
//
// });
executorService.execute(new MyTask(latch));
}
try {
latch.await(); // 等待所有異步任務(wù)執(zhí)行完成
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 執(zhí)行后續(xù)處理邏輯
}
static class MyTask implements Runnable {
private CountDownLatch latch;
public MyTask(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
try {
} catch (Exception e) {
} finally {
latch.countDown();
}
}
}
3. Semaphore(信號(hào)量)
Semaphore 是一個(gè)用于控制同時(shí)訪問(wèn)特定資源的線程數(shù)量的同步工具。它通過(guò)維護(hù)一個(gè)許可集來(lái)管理對(duì)資源的訪問(wèn)。線程在訪問(wèn)資源之前必須從信號(hào)量中獲取許可,訪問(wèn)完成后釋放許可。如果沒(méi)有可用的許可,線程將被阻塞,直到有可用的許可為止。
/**
* 控制并發(fā)執(zhí)行的任務(wù)數(shù)量
*/
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
Semaphore semaphore = new Semaphore(10);
// 模擬100個(gè)附件同時(shí)上傳
for (int i = 0; i < 100; i++) {
executorService.execute(()->{
try {
semaphore.acquire();
upload();
} catch (Exception e) {
} finally {
semaphore.release();
}
});
}
executorService.shutdown();
}
/**
* 附件上傳操作
*/
public static void upload() {
// 假設(shè),最多同時(shí)處理10個(gè)附件,太多的話可能會(huì)內(nèi)存溢出,為了保護(hù)它,不讓它掛掉,我們可以控制并發(fā)請(qǐng)求數(shù)量
// ......
}
上面的例子,我們?cè)谡{(diào)用端限制并發(fā)請(qǐng)求數(shù)來(lái)達(dá)到保護(hù)被調(diào)用方的目的,其實(shí)也可以寫(xiě)在被調(diào)用端,效果是一樣的,在調(diào)用方和被調(diào)用方其中一方做控制就行。
4. Redisson分布式鎖和同步器
Redisson 是 Redis 的Java客戶端,在分布式環(huán)境下,Redission實(shí)現(xiàn)了Semaphore和CountDownLatch。
https://redisson.org/docs/data-and-services/locks-and-synchronizers/

<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.41.0</version>
</dependency>
Semaphore基本用法
RSemaphore semaphore = redisson.getSemaphore("mySemaphore");
// acquire single permit
semaphore.acquire();
// or acquire 10 permits
semaphore.acquire(10);
// or try to acquire permit
boolean res = semaphore.tryAcquire();
// or try to acquire permit or wait up to 15 seconds
boolean res = semaphore.tryAcquire(15, TimeUnit.SECONDS);
// or try to acquire 10 permit
boolean res = semaphore.tryAcquire(10);
// or try to acquire 10 permits or wait up to 15 seconds
boolean res = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
semaphore.release();
}
}
CountDownLatch基本用法
RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch");
latch.trySetCount(1);
// await for count down
latch.await();
// in other thread or JVM
RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch");
latch.countDown();
參考
https://blog.csdn.net/weixin_42373241/article/details/139441473

浙公網(wǎng)安備 33010602011771號(hào)