多线程开发常见问题汇总
1. Thread.UncaughtExceptionHandler
UncaughtExceptionHandler 是一个接口,用于处理线程因未捕获异常而突然终止的情况。
虽然,通常都会在线程执行的代码中加try...catch来捕获异常,那么如果某些异常没有被catch住(比如,线程突然死掉了)那么我们将不知道发生了什么。因此,给每个现在设置一个未捕获异常处理器很有必要。
@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) {
// 设置全局默认的未捕获异常处理器
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
Thread thread = new Thread(() -> {
int a = 1 / 0;
});
// 给某个线程设置自己的未捕获异常处理器
thread.setUncaughtExceptionHandler(((t, e) -> {
System.out.println("线程执行异常!线程名称: " + t.getName());
logger.error("线程执行异常!名称: {}", 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(倒计时)
CountDownLatch 是 Java 中的一个同步工具类,它允许一个或多个线程等待其他线程完成操作。
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个任务
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(); // 等待所有异步任务执行完成
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 执行后续处理逻辑
}
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(信号量)
Semaphore 是一个用于控制同时访问特定资源的线程数量的同步工具。它通过维护一个许可集来管理对资源的访问。线程在访问资源之前必须从信号量中获取许可,访问完成后释放许可。如果没有可用的许可,线程将被阻塞,直到有可用的许可为止。
/**
* 控制并发执行的任务数量
*/
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
Semaphore semaphore = new Semaphore(10);
// 模拟100个附件同时上传
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() {
// 假设,最多同时处理10个附件,太多的话可能会内存溢出,为了保护它,不让它挂掉,我们可以控制并发请求数量
// ......
}
上面的例子,我们在调用端限制并发请求数来达到保护被调用方的目的,其实也可以写在被调用端,效果是一样的,在调用方和被调用方其中一方做控制就行。
4. Redisson分布式锁和同步器
Redisson 是 Redis 的Java客户端,在分布式环境下,Redission实现了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