复制代码

为懒人提供无限可能,生命不息,code不止

人类感性的情绪,让我们知难行难
我思故我在
日拱一卒,功不唐捐
  • 首页
  • 前端
  • 后台
  • 数据库
  • 运维
  • 资源下载
  • 实用工具
  • 接口文档工具
  • 登录
  • 注册

JAVA代码

【原创】在chatGPT的指导下学习java 线程池

作者: whooyun发表于: 2023-04-06 18:24



问:java 实现一个线程池调用

答:

好的,以下是一个使用 Java 线程池的示例代码,它可以异步执行多个任务:

				
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建一个具有固定线程数(5个)的线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);
        // 提交任务
        for (int i = 0; i < 10; i++) {
            executor.execute(new MyTask(i));
        }
        // 关闭线程池
        executor.shutdown();
    }
}
class MyTask implements Runnable {
    private int taskId;
    public MyTask(int taskId) {
        this.taskId = taskId;
    }
    public void run() {
        System.out.println("任务 " + taskId + " 开始执行");
        try {
            Thread.sleep(2000); // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("任务 " + taskId + " 执行完毕");
    }
}

在这个示例中,我们首先使用Executors.newFixedThreadPool(5)创建一个具有固定线程数(5个)的线程池。然后使用executor.execute()方法提交多个任务,每个任务都是一个MyTask实例。MyTask类实现了Runnable接口,其中的run()方法模拟了一个需要执行 2 秒的任务。最后,我们调用executor.shutdown()方法关闭线程池。您可以根据需要修改该示例以适应您的具体情况。

问:Executors.newFixedThreadPool(5) 可能会有问题

答:

是的,您提到的问题是可能存在的。在使用Executors.newFixedThreadPool(5)创建固定大小的线程池时,如果提交的任务过多,线程池可能会出现线程饥饿的问题。 具体来说,如果线程池中的所有线程都在执行长时间的任务,而且提交的任务数量很多,那么新提交的任务可能需要等待线程池中的某个线程空闲下来才能执行。如果等待的时间过长,就可能会出现线程饥饿的情况,即某些任务无法得到及时的执行。 为了避免线程饥饿的问题,我们可以使用更加灵活的线程池,例如ThreadPoolExecutor。ThreadPoolExecutor可以通过设置核心线程数、最大线程数、任务队列等参数来实现灵活的线程池管理。例如,以下代码创建一个核心线程数为 5,最大线程数为 10,任务队列容量为 100 的线程池:

				
				
plaintextCopy code
ExecutorService executor = new ThreadPoolExecutor(
    5, // 核心线程数
    10, // 最大线程数
    0L, // 空闲线程存活时间
    TimeUnit.MILLISECONDS, // 时间单位
    new LinkedBlockingQueue<Runnable>(100) // 任务队列
);

在这个示例中,我们使用ThreadPoolExecutor创建了一个具有 5 个核心线程、最大线程数为 10、任务队列容量为 100 的线程池。当线程池中的线程数量达到核心线程数时,新的任务会被放入任务队列中等待执行。当任务队列已满,且线程数还未达到最大线程数时,新的任务会新建线程来执行。如果线程数已经达到最大线程数,新的任务会被拒绝执行。这样可以保证线程池的灵活性,并避免出现线程饥饿的问题。

问:

new ThreadPoolExecutor 没有指定拒绝策略

答:

是的,您提到的问题是正确的。在使用ThreadPoolExecutor创建线程池时,如果没有指定拒绝策略,那么当任务队列已满,且线程数已经达到最大线程数时,新的任务就会抛出RejectedExecutionException异常,导致任务无法执行。 为了避免这个问题,我们可以使用RejectedExecutionHandler接口来指定线程池的拒绝策略。RejectedExecutionHandler接口有一个rejectedExecution()方法,用于处理被拒绝的任务。通常情况下,我们可以使用以下几种拒绝策略:

  • AbortPolicy:抛出RejectedExecutionException异常,阻止系统正常运行。
  • CallerRunsPolicy:由提交任务的线程来执行该任务,可以降低新任务的流量。
  • DiscardPolicy:直接丢弃被拒绝的任务,不做任何处理。
  • DiscardOldestPolicy:丢弃任务队列中最早的任务,然后尝试将新任务添加到队列中。 例如,以下代码创建了一个核心线程数为 5,最大线程数为 10,任务队列容量为 100,拒绝策略为CallerRunsPolicy的线程池:
				
				
plaintextCopy code
ExecutorService executor = new ThreadPoolExecutor(
    5, // 核心线程数
    10, // 最大线程数
    0L, // 空闲线程存活时间
    TimeUnit.MILLISECONDS, // 时间单位
    new LinkedBlockingQueue<Runnable>(100), // 任务队列
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

在这个示例中,我们使用ThreadPoolExecutor创建了一个具有 5 个核心线程、最大线程数为 10、任务队列容量为 100,拒绝策略为CallerRunsPolicy的线程池。当任务队列已满,且线程数已经达到最大线程数时,新的任务会由提交任务的线程来执行。这样可以降低新任务的流量,避免出现任务丢失的问题。您可以根据需要选择合适的拒绝策略。