线程池的定义,以及简单的使用,Runable和Callable的区别

定义线程池对象

public class SingletonThreadPool {
    //核心线程参数
    private static final int CORE_POOL_SIZE = 10;
    //最大线程数
    private static final int MAXIMUM_POOL_SIZE = 50;
    //时间
    private static final long KEEP_ALIVE_TIME = 3;
    //时间单位
    private static final TimeUnit UNIT = TimeUnit.MINUTES;
    //工作队列
    private static final BlockingQueue<Runnable> WORK_QUEUE = new ArrayBlockingQueue<>(50);
    //对象
    private static final ThreadPoolExecutor EXECUTOR;

    // 私有构造器,防止外部通过new创建实例
    private SingletonThreadPool() {
    }

    // 静态初始化块,创建线程池实例
    static {
        EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, UNIT, WORK_QUEUE,
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

    // 提供全局访问点获取线程池实例
    public static ThreadPoolExecutor getExecutor() {
        return EXECUTOR;
    }
}

Service层

@Transactional
    public synchronized String createDataCenter(DataCenterSaveReqVO createReqVO) {
        createReqVO.setId(IdUtil.getSnowflakeNextIdStr());
        // 插入
        DataCenterDO dataCenter = BeanUtils.toBean(createReqVO, DataCenterDO.class);
        //创建Runable类
        ThreadPoolExecutor executor = SingletonThreadPool.getExecutor();
        //发送
        executor.execute(new AlarmDataCheck(alarmSettingMapper,alarmDataMapper,dataCenter,dataCenterMapper,alarmDataProcessingMapper,partInfoMapper));
        // 返回
        return dataCenter.getId();
    }

为什么要使用

executor.execute(); 没有返回值,因为线程对象对该方法的返回值设置为void,而且如果方法中抛出的异常,也不会终止当前方法,他的传入对象也必须为Runable或者实现了Runable的对象;
executor.submit(); 有返回值,其中对象必须是且Callable才行,或者实现了Callable的对象才可以;

很多人没搞懂,为什么可以传入对象,现在甚至很多人对忘记了,匿名内部类的使用

executor.execute(new Runnable() {
           @Override
           public void run() {
               
           }
       });

反而现在使用了Lambda,都忘记了里面传入的是什么对象

executor.execute(()->{});

其实在这里系统会默认给你匹配,是Runable还是Callable对象

但是,我这里直接传入了一个实现了Runable的对象,为什么呢,因为这样所有的逻辑都存在一个对象中,更加易于维护和拓展

@Component
@NoArgsConstructor
@Scope("prototype")
public class AlarmDataCheck implements Runnable{
    
    public AlarmDataCheck(AlarmSettingMapper alarmSettingMapper,
                          AlarmDataMapper alarmDataMapper,
                          DataCenterDO dataCenter,
                          DataCenterMapper dataCenterMapper,
                          AlarmDataProcessingMapper alarmDataProcessingMapper,
                          PartInfoMapper partInfoMapper) {
        this.alarmSettingMapper = alarmSettingMapper;
        this.alarmDataMapper = alarmDataMapper;
        this.dataCenter = dataCenter;
        this.dataCenterMapper = dataCenterMapper;
        this.alarmDataProcessingMapper = alarmDataProcessingMapper;
        this.partInfoMapper = partInfoMapper;
    }

    //通过有参构造像alarmSettingDO传入对象
    @Override
    public void run() {
        dataCenterMapper.insert(dataCenter);
        if (warningChecks(dataCenter)){
            dataCenter.setReserveTwo(alarmSettingDO.getReserveOne()).setIsAlarming("1");
            dataCenterMapper.updateById(dataCenter);
        }
        //先获取传感器id,根据传感器id查询
        PartInfoDO partInfoDO = partInfoMapper.selectOne(PartInfoDO::getSensorId, dataCenter.getReserveOne());
        partInfoDO.setState(alarmSettingDO == null ?"0":"3".equals(alarmSettingDO.getReserveOne())?"4":alarmSettingDO.getReserveOne());
        //更新部件磨损状态
        partInfoMapper.updateById(partInfoDO);
    }

在这里由于线程池的启动太快原因导致无法自动注入,我采取了有参构造的方法,我试过懒加载不好使,如果大家,有什么好的方法,欢迎提供

以下是submit的使用教程,以及对应的方法,我的建议是根据情况选择是否要采用submit方法,因为他可以校验线程池中的任务是否执行完毕

<think>我们根据用户的问题,需要回答两个部分: 1. Java中通过线程池创建线程的方法有哪些? 2. RunnableCallable接口的区别是什么? 结合引用内容,我们可以总结如下: 一、通过线程池创建线程的方法(主要参考引用[2]引用[5]): 1. 使用Executors工具类提供的工厂方法(快速创建): - newFixedThreadPool(int nThreads): 创建固定大小的线程池 - newCachedThreadPool(): 创建可缓存的线程池线程池大小可根据需要自动调整 - newSingleThreadExecutor(): 创建单线程的线程池 - newScheduledThreadPool(int corePoolSize): 创建支持定时及周期性任务执行的线程池 2. 直接使用ThreadPoolExecutor类(自定义创建): 通过ThreadPoolExecutor的构造函数创建,可以更精确地控制线程池的参数(核心线程数、最大线程数、存活时间、任务队列、线程工厂、拒绝策略)。 二、RunnableCallable接口的区别(主要参考引用[1]、引用[3]引用[4]): 1. 返回值: - Runnable:没有返回值,run()方法返回void。 - Callable:有返回值,call()方法返回一个泛型类型的值(Future对象可以获取该返回值)。 2. 异常处理: - Runnable:run()方法不能抛出受检异常,只能在方法内部处理。 - Callable:call()方法可以抛出受检异常。 3. 使用方式: - Runnable:通常与Thread类结合使用,或者提交给线程池的execute()方法。 - Callable:只能提交给线程池的submit()方法(因为需要返回Future对象)。 下面我们按照要求组织回答。</think>### Java线程池创建线程的方法及Runnable与Callable接口区别 #### 一、线程池创建线程的方法 1. **通过`Executors`工具类创建**(推荐测试环境使用) ```java // 固定大小线程池 ExecutorService fixedPool = Executors.newFixedThreadPool(4); // 单线程池(保证任务顺序执行) ExecutorService singlePool = Executors.newSingleThreadExecutor(); // 可缓存线程池(自动扩容回收) ExecutorService cachedPool = Executors.newCachedThreadPool(); // 定时任务线程池 ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2); ``` 2. **通过`ThreadPoolExecutor`自定义创建**(生产环境推荐) ```java ThreadPoolExecutor customPool = new ThreadPoolExecutor( 5, // 核心线程数 10, // 最大线程数 60, // 线程空闲存活时间(秒) TimeUnit.SECONDS, // 时间单位 new ArrayBlockingQueue<>(100), // 任务队列 Executors.defaultThreadFactory(), // 线程工厂 new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 ); ``` 3. **提交任务到线程池** ```java // 提交Runnable任务 customPool.execute(() -> System.out.println("Runnable任务")); // 提交Callable任务 Future<String> future = customPool.submit(() -> { Thread.sleep(1000); return "Callable结果"; }); ``` > **关键点**: > - `execute()`仅提交`Runnable`任务(无返回值) > - `submit()`可提交`Runnable`或`Callable`任务(后者返回`Future`对象)[^4] --- #### 二、Runnable与Callable接口区别 | 特性 | Runnable接口 | Callable接口 | |---------------------|----------------------------------|----------------------------------| | **返回值** | `void run()` 无返回值 | `V call()` 返回泛型结果 | | **异常处理** | 必须内部处理受检异常 | 可直接抛出受检异常 | | **使用场景** | 简单异步任务(如日志写入) | 需获取结果的任务(如计算返回值) | | **线程池提交方式** | `execute()` 或 `submit()` | 只能通过 `submit()` 提交 | | **结果获取** | 无法直接获取执行结果 | 通过 `Future.get()` 获取结果 | **代码示例对比**: ```java // Runnable实现 class PrintTask implements Runnable { @Override public void run() { System.out.println("执行Runnable任务"); } } // Callable实现 class CalcTask implements Callable<Integer> { @Override public Integer call() throws Exception { return 1 + 1; // 返回计算结果 } } // 使用示例 ExecutorService pool = Executors.newFixedThreadPool(2); pool.execute(new PrintTask()); // Runnable提交 Future<Integer> future = pool.submit(new CalcTask()); // Callable提交 System.out.println("计算结果:" + future.get()); // 输出: 计算结果:2 ``` > **核心区别**: > - `Runnable` 适用于**无返回值的简单任务** > - `Callable` 适用于**需要返回值或异常抛出的复杂任务**(如数据库查询、远程调用)[^3] --- #### 三、最佳实践建议 1. **线程池选择**: - CPU密集型任务 → `FixedThreadPool`(核心线程数=CPU核数+1) - IO密集型任务 → `CachedThreadPool`(避免线程阻塞) 2. **接口选择原则**: ```mermaid graph LR A[需要返回值?] -->|是| B[使用Callable] A -->|否| C{需要抛出受检异常?} C -->|是| B C -->|否| D[使用Runnable] ``` 3. **资源管理**: ```java // 正确关闭线程池 pool.shutdown(); // 温关闭(等待任务完成) // pool.shutdownNow(); // 立即关闭(中断所有线程) ``` > **生产环境警告**:避免使用`Executors.newFixedThreadPool()`的无界队列,推荐自定义`ThreadPoolExecutor`并设置**有界队列**防止OOM[^2]。 --- #### 四、执行流程对比 **Runnable任务流程**: ```mermaid sequenceDiagram Main Thread->>ThreadPool: execute(Runnable) ThreadPool->>Worker Thread: 分配任务 Worker Thread->>Runnable: 执行run() Worker Thread-->>ThreadPool: 任务完成(无返回值) ``` **Callable任务流程**: ```mermaid sequenceDiagram Main Thread->>ThreadPool: submit(Callable) ThreadPool->>Future: 返回Future对象 ThreadPool->>Worker Thread: 分配任务 Worker Thread->>Callable: 执行call() Worker Thread->>Future: 存储结果/异常 Main Thread->>Future: future.get() 获取结果 ``` [^1]: RunnableCallableJava创建线程的两种核心接口 [^2]: Executors工具类创建线程池存在资源耗尽风险 [^3]: Callable通过Future对象获取返回值并支持异常抛出 [^4]: 线程池使用submit()提交Callable任务 [^5]: Runnable任务通过execute()或submit()提交
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值