多线程开发常见问题汇总

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 

  

 

posted @ 2024-12-31 09:51  废物大师兄  阅读(382)  评论(0编辑  收藏  举报