Java线程池的这几个大坑,你踩过几个?

首先看一个简单的例子:代码可能会抛出空指针异常,但这个异常就会被吞掉。

图片

要优雅解决问题,可以为线程池设置一个全局的异常处理器,使用自定义线程工厂来设置!

 

java

public class CustomThreadFactory implements ThreadFactory {
private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();

@Override
public Thread newThread(Runnable r) {
Thread thread = defaultFactory.newThread(r);

// 设置全局异常处理器
thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println("Thread " + t.getName() + " threw exception: " + e.getMessage());
});
return thread;
}

public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2, new CustomThreadFactory());
// 提交任务到线程池
executorService.execute(() -> {
throw new NullPointerException("Test Exception");
});
// 关闭线程池
executorService.shutdown();
}
}

拒绝策略设置错误导致接口超时!

如果没有正确设置拒绝策略,可能会导致接口超时或服务中断。

 

java

public class RejectionPolicyExample {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
1, // 核心线程数
1, // 最大线程数
0L, // 空闲线程存活时间
TimeUnit.MILLISECONDS, // 存活时间单位
new LinkedBlockingQueue<>(1), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 默认拒绝策略
);

// 提交三个任务到线程池,第三个任务将被拒绝
for (int i = 0; i < 3; i++) {
executorService.execute(() -> {
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " executed task");
});
}

// 关闭线程池
executorService.shutdown();
}
}

合适的拒绝策略如CallerRunsPolicy可以使任务在调用者线程中执行,从而避免任务丢失。

图片

 

java

public class CallerRunsPolicyExample {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
1, // 核心线程数
1, // 最大线程数
0L, // 空闲线程存活时间
TimeUnit.MILLISECONDS, // 存活时间单位
new LinkedBlockingQueue<>(1), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 调用者运行策略
);

// 提交三个任务到线程池,第三个任务将在调用者线程中执行
for (int i = 0; i < 3; i++) {
executorService.execute(() -> {
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " executed task");
});
}

// 关闭线程池
executorService.shutdown();
}
}

重复创建线程池导致内存溢出

重复创建线程池会导致系统资源浪费,甚至引发内存溢出。

 

java

public class DuplicateThreadPoolExample {
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
ExecutorService executorService = Executors.newFixedThreadPool(10);

// 提交任务到线程池
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + " executed task");
});

// 关闭线程池
executorService.shutdown();
}
}
}

那当然我们整个应用中只使用一个线程池实例。

 

java

public class SingletonThreadPoolExample {
// 单例线程池
private static final ExecutorService executorService = Executors.newFixedThreadPool(10);

public static ExecutorService getExecutorService() {
return executorService;
}

public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
// 使用单例线程池提交任务
getExecutorService().execute(() -> {
System.out.println(Thread.currentThread().getName() + " executed task");
});
}

// 关闭线程池
getExecutorService().shutdown();
}
}

共用线程池执行不同任务

在同一个线程池中执行不同性质的任务,可能会导致任务相互影响,进而降低系统效率。例如,CPU密集型IO密集型任务混用同一线程池,效率会大打折扣。

 

java

public class MixedTasksExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 提交CPU密集型任务
executorService.execute(() -> {
for (int i = 0; i < 1000000; i++) {} // 模拟CPU密集型任务
System.out.println(Thread.currentThread().getName() + " executed CPU-intensive task");
});
// 提交IO密集型任务
executorService.execute(() -> {
try {
Thread.sleep(2000); // 模拟IO密集型任务
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " executed IO-intensive task");
});
// 关闭线程池
executorService.shutdown();
}
}

解决方案:

为不同任务分配独立的线程池,确保任务执行的高效性。

 

java

public class SeparateTasksExample {
// CPU密集型任务的线程池
private static final ExecutorService cpuIntensivePool = Executors.newFixedThreadPool(5);
// IO密集型任务的线程池
private static final ExecutorService ioIntensivePool = Executors.newFixedThreadPool(5);

public static void main(String[] args) {
// 提交CPU密集型任务
cpuIntensivePool.execute(() -> {
for (int i = 0; i < 1000000; i++) {} // 模拟CPU密集型任务
System.out.println(Thread.currentThread().getName() + " executed CPU-intensive task");
});

// 提交IO密集型任务
ioIntensivePool.execute(() -> {
try {
Thread.sleep(2000); // 模拟IO密集型任务
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " executed IO-intensive task");
});

// 关闭线程池
cpuIntensivePool.shutdown();
ioIntensivePool.shutdown();
}
}

ThreadLocal与线程池的冲突

因为线程池中的线程是复用的,ThreadLocal中的变量可能会被其他任务不小心修改未及时清理

 

java

public class ThreadLocalIssueExample {
private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);

// 提交任务到线程池
for (int i = 0; i < 5; i++) {
executorService.execute(() -> {
int value = threadLocal.get();
System.out.println(Thread.currentThread().getName() + " initial value: " + value);
threadLocal.set(value + 1);
System.out.println(Thread.currentThread().getName() + " updated value: " + threadLocal.get());
});
}

// 关闭线程池
executorService.shutdown();
}
}

那当然在任务执行完毕后,及时清理ThreadLocal变量

public class ThreadLocalSolutionExample {
    private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        
        // 提交任务到线程池
        for (int i = 0; i < 5; i++) {
            executorService.execute(() -> {
                try {
                    int value = threadLocal.get();
                    System.out.println(Thread.currentThread().getName() + " initial value: " + value);
                    threadLocal.set(value + 1);
                    System.out.println(Thread.currentThread().getName() + " updated value: " + threadLocal.get());
                } finally {
                    // 任务完成后清理ThreadLocal变量
                    threadLocal.remove();
                }
            });
        }
        
        // 关闭线程池
        executorService.shutdown();
    }
}

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值