关于《Java并发编程之线程池十八问》的补充内容

简介: 【6月更文挑战第6天】关于《Java并发编程之线程池十八问》的补充内容

一、写在开头

在上一篇文章我们写《Java并发编程之线程池十八问》的时候,鉴于当时的篇幅已经过长,很多内容就没有扩展了,在这篇文章里对一些关键知识点进行对比补充。

二、Runnable vs Callable

在创建线程的时候,一般会选用 RunnableCallable 两种方式。

【源码对比】

Runnable接口

@FunctionalInterface
public interface Runnable {
   
   
   /**
    * 被线程执行,没有返回值也无法抛出异常
    */
    public abstract void run();
}

Callable接口

@FunctionalInterface
public interface Callable<V> {
   
   
    /**
     * 计算结果,或在无法这样做时抛出异常。
     * @return 计算得出的结果
     * @throws 如果无法计算结果,则抛出异常
     */
    V call() throws Exception;
}
  1. Runnable自 Java 1.0 以来一直存在,Callable在 Java 1.5 时引入;
  2. Runnable 接口不会返回结果或抛出检查异常,Callable 接口可以;
  3. Callable支持泛型,可定义返回值类型,但一般情况下没有返回值时,我们推荐使用Runnable接口,使得代码更简洁!
  4. 工具类 Executors 可以实现将 Runnable 对象转换成 Callable 对象。(Executors.callable(Runnable task) 或 Executors.callable(Runnable task, Object result))。

三、execute() vs submit()

在线程池中我们有两种提交任务的方式,分别是 execute()submit(),虽然我们在上一篇文章中都有用到,但是并没对它们的特点进行总结,这里做一个对比:

  1. execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
  2. submit()方法用于提交需要返回值的任务。线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功,并且可以通过 Future 的 get()方法来获取返回值。
//这里使用Executors只是方便测试,正常使用时推荐使用ThreadPoolExecutor!
ExecutorService executorService = Executors.newFixedThreadPool(3);

Future<String> submit = executorService.submit(() -> {
   
   
    try {
   
   
        Thread.sleep(5000L);
    } catch (InterruptedException e) {
   
   
        e.printStackTrace();
    }
    return "javabuild";
});

String s = submit.get();
System.out.println(s);
executorService.shutdown();

输出:

javabuild

如果一直没有获取到返回结果,会报错,使用get(long timeout,TimeUnit unit)方法的话,如果在 timeout 时间内任务还没有执行完,就会抛出 java.util.concurrent.TimeoutException。

四、shutdown() vs shutdownNow()

在JDK 1.8 中,线程池的停止一般使用 shutdown()、shutdownNow()这两种方法。

方法一: shutdown()
public void shutdown() {
   
   
    final ReentrantLock mainLock = this.mainLock; // ThreadPoolExecutor的主锁
    mainLock.lock(); // 加锁以确保独占访问

    try {
   
   
        checkShutdownAccess(); // 检查是否有关闭的权限
        advanceRunState(SHUTDOWN); // 将执行器的状态更新为SHUTDOWN
        interruptIdleWorkers(); // 中断所有闲置的工作线程
        onShutdown(); // ScheduledThreadPoolExecutor中的挂钩方法,可供子类重写以进行额外操作
    } finally {
   
   
        mainLock.unlock(); // 无论try块如何退出都要释放锁
    }
    tryTerminate(); // 如果条件允许,尝试终止执行器
}

在shutdown的源码中,会启动一次顺序关闭,在这次关闭中,执行器不再接受新任务,但会继续处理队列中的已存在任务,当所有任务都完成后,线程池中的线程会逐渐退出。

方法二: shutdown()
/**
 * 尝试停止所有正在执行的任务,停止处理等待的任务,
 * 并返回等待处理的任务列表。
 *
 * @return 从未开始执行的任务列表
 */
public List<Runnable> shutdownNow() {
   
   
    List<Runnable> tasks; // 用于存储未执行的任务的列表
    final ReentrantLock mainLock = this.mainLock; // ThreadPoolExecutor的主锁
    mainLock.lock(); // 加锁以确保独占访问
    try {
   
   
        checkShutdownAccess(); // 检查是否有关闭的权限
        advanceRunState(STOP); // 将执行器的状态更新为STOP
        interruptWorkers(); // 中断所有工作线程
        tasks = drainQueue(); // 清空队列并将结果放入任务列表中
    } finally {
   
   
        mainLock.unlock(); // 无论try块如何退出都要释放锁
    }
    tryTerminate(); // 如果条件允许,尝试终止执行器
    return tasks; // 返回队列中未被执行的任务列表
}

与shutdown不同的是shutdownNow会尝试终止所有的正在执行的任务,清空队列,停止失败会抛出异常,并且返回未被执行的任务列表。

五、isTerminated() vs isShutdown()

  1. isShutDown 当调用 shutdown() 或shutdownNow()方法后返回为 true;
  2. isTerminated 当调用 shutdown() 方法后,并且所有提交的任务完成后返回为 true;当调用shutdownNow()方法后,成功停止后返回true;
  3. 当线程池任务都正常完成的话,则这两种方法均为false。

image.png

目录
相关文章
|
1天前
|
监控 Java
Java并发编程:深入理解线程池
在Java并发编程领域,线程池是提升应用性能和资源管理效率的关键工具。本文将深入探讨线程池的工作原理、核心参数配置以及使用场景,通过具体案例展示如何有效利用线程池优化多线程应用的性能。
|
3天前
|
安全 Java 调度
Java中的并发编程:从基础到高级
【7月更文挑战第20天】在Java的世界中,并发编程是一块重要的领域,它允许多个操作同时执行,极大地提高了程序的效率和性能。本文将深入探讨Java并发编程的核心概念,从线程的基础使用到高级的并发工具类,再到实际案例分析,旨在为读者提供一个全面而深入的视角来理解和掌握Java并发编程的艺术。
|
12天前
|
Java 测试技术 开发者
Java面试题:解释CountDownLatch, CyclicBarrier和Semaphore在并发编程中的使用
Java面试题:解释CountDownLatch, CyclicBarrier和Semaphore在并发编程中的使用
34 11
|
9天前
|
Java 调度 开发者
Java中的并发编程:从基础到高级
【7月更文挑战第14天】在Java的世界中,并发编程是提升应用性能和响应能力的关键。本文将带领读者从线程的基础概念出发,深入探讨Java内存模型,逐步过渡到高级并发工具类如Executors框架和并发集合,最后通过案例分析展示如何在实际开发中运用这些知识解决并发问题。文章旨在为初学者提供清晰的学习路径,同时为有经验的开发者提供深度参考。
17 4
|
9天前
|
安全 Java 开发者
Java并发编程中的线程安全性与性能优化
在Java编程中,处理并发问题是至关重要的。本文探讨了Java中线程安全性的概念及其在性能优化中的重要性。通过深入分析多线程环境下的共享资源访问问题,结合常见的并发控制手段和性能优化技巧,帮助开发者更好地理解和应对Java程序中的并发挑战。 【7月更文挑战第14天】
|
9天前
|
监控 Java API
Java并发编程之线程池深度解析
【7月更文挑战第14天】在Java并发编程领域,线程池是提升性能、管理资源的关键工具。本文将深入探讨线程池的核心概念、内部工作原理以及如何有效使用线程池来处理并发任务,旨在为读者提供一套完整的线程池使用和优化策略。
|
12天前
|
缓存 安全 Java
Java中线程池如何管理?
【7月更文挑战第11天】Java中线程池如何管理?
21 2
|
11天前
|
存储 安全 算法
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第72天】 在现代软件开发中,尤其是Java应用开发领域,并发编程是一个无法回避的重要话题。随着多核处理器的普及,合理利用并发机制对于提高软件性能、响应速度和资源利用率具有重要意义。本文旨在探讨Java并发编程的核心概念、线程安全的策略以及性能优化技巧,帮助开发者构建高效且可靠的并发应用。通过实例分析和理论阐述,我们将揭示在高并发环境下如何平衡线程安全与系统性能之间的关系,并提出一系列最佳实践方法。
|
12天前
|
监控 Java 调度
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
24 1
|
12天前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
23 1