Java—线程池

简介: Java—线程池

线程池


线程池基本概念

线程池

线程池本质上是一种对象池,用于管理线程资源。在任务执行前,需要从线程池中拿出线程来执行。在任务执行完成之后,把线程放回线程池。实际开发中,线程资源一般通过线程池提供,比如处理数据库连接、接收网络请求。

  1. 线程的创建更加规范,可以合理控制开辟线程的数量。
  2. 不必频繁地创建和销毁线程,优化了资源的开销。

核心线程池

(corePool) 通常状况下,线程池最多能创建的线程数。

当有新任务等待处理时,线程池会首先判断核心线程池是否已满,如果没满则创建线程执行任务。即使有其他核心线程空闲也会创建新的核心线程来执行。

任务队列

(BlockQueue) 线程池中等待被线程执行的任务队列。

如果核心线程池已满,线程池会判断队列是否已满。如果队列没满,就会将任务放在队列中等待执行。

  • ArrayBlockingQueue // 基于数组实现的阻塞队列,有界。
  • LinkedBlockingQueue // 基于链表实现的阻塞队列,可以无界。
  • SynchronousQueue // 不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作。
  • PriorityBlockingQueue // 带优先级的阻塞队列,无界。

最大线程池

(maximumPool) 任务量很大时,线程池最多能创建的线程数。

如果队列已满,说明当前任务量已经非常大,仅靠核心线程池内的线程数量已无法处理。线程池会判断最大线程池是否已满,如果没满则创建更多线程,从等待队列首部取得任务并执行。

拒绝策略

(RejectedExecutionHandler) 线程池拒绝过量任务的方式。

如果最大线程池已满,表示当前服务器已无法处理这么多任务。任务会按照既定的拒绝策略被处理。

  • CallerRunsPolicy // 在调用者线程执行。
  • AbortPolicy // 直接抛出 RejectedExecutionException 异常。
  • DiscardPolicy // (常用)任务直接丢弃,不做任何处理。
  • DiscardOldestPolicy // 丢弃队列里最旧的那个任务,再尝试执行当前任务。

ThreadPoolExecutor 类

实现了 ExecutorService 接口,是 java 开发常用的线程池类。位于 java.util.concurrent 包内,使用时需要进行导入。

创建线程池

  1. ThreadPoolExecutor 类在创建线程池时需要输入以下参数:
int corePoolSize = 2;                                                // 核心线程池大小
int maximumPoolSize = 4;                                             // 最大线程池大小
long keepAliveTime = 10;                                             // 空闲线程多久被销毁,0 表示永远不会
TimeUnit unit = TimeUnit.SECONDS;                                    // keepAliveTime 的单位
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);     // 任务队列
ThreadFactory threadFactory = new NameTreadFactory();                // 线程工厂接口,一般默认。
RejectedExecutionHandler handler = new MyIgnorePolicy();             // 拒绝策略,一般默认。
ThreadPoolExecutor service = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, 
unit, workQueue, threadFactory, handler);Copy to clipboardErrorCopied
复制代码
  1. ThreadPoolExecutor 类还可以重写以下方法(默认为空实现):
ExecutorService service = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1)) {
    // 任务执行前被调用
    @Override 
    protected void beforeExecute(Thread t, Runnable r) {
        System.out.println("beforeExecute is called");
    }
    // 任务执行后被调用
    @Override 
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println("afterExecute is called");
    }
    // 线程池结束后被调用
    @Override
    protected void terminated() {
        System.out.println("terminated is called");
    }
};Copy to clipboardErrorCopied
复制代码

获取线程池信息

service.getTaskCount();                 // 获取已经执行或正在执行的任务数
service.getCompletedTaskCount();        // 获取已经执行的任务数
service.getLargestPoolSize();           // 获取线程池曾经创建过的最大线程数
service.getPoolSize();                  // 获取线程池线程数
service.getActiveCount();               // 获取活跃线程数(正在执行任务的线程数)Copy to clipboardErrorCopied
复制代码

提交任务

可以向线程池提交的任务有两种:Runnable 接口和 Callable 接口。

Runnable 接口

内部定义了 run 方法,没有返回值,不允许抛出异常。通过 execute 方法向线程池提交。

service.execute(new Runnable(){             
    System.out.println("new thread");
});Copy to clipboardErrorCopied
复制代码

Callable 接口

内部定义了 call 方法,允许有返回值,允许抛出异常。通过 submit 方法向线程池提交,返回一个 Future 对象。

可以通过调用 Future 对象的 get 方法获得数据,在返回结果前 get 方法会阻塞。

Future<Integer> f = service.submit(new Callable(){            
    System.out.println("new thread");
    return 1;
});
System.out.println(f.get());Copy to clipboardErrorCopied
复制代码

关闭线程池

service.shutdown();      // 线程池不再接受新的任务,线程池中已有任务执行完成后终止。
service.shutdownNow();   // 线程池不再接受新的任务并对所有线程执行 interrupt 操作,清空队列并终止。
boolean b = service.isShutdown();      // 返回线程池是否关闭:不再接受新任务。
boolean b = service.isTerminated();    // 返回线程池是否终止Copy to clipboardErrorCopied
复制代码

ThreadPoolExecutor 类示例

public class ThreadPool {
    private static ExecutorService pool;
    public static void main( String[] args )
    {
        //自定义线程工厂
        pool = new ThreadPoolExecutor(2, 4, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5),
                new ThreadFactory() {
            public Thread newThread(Runnable r) {
                System.out.println("线程"+r.hashCode()+"创建");
                //线程命名
                Thread th = new Thread(r,"threadPool"+r.hashCode());
                return th;
            }
        }, new ThreadPoolExecutor.CallerRunsPolicy());
        for(int i=0;i<10;i++) {
            pool.execute(new ThreadTask());
        }    
    }
}
public class ThreadTask implements Runnable{    
    public void run() {
        //输出执行线程的名称
        System.out.println("ThreadName:"+Thread.currentThread().getName());
    }
}Copy to clipboardErrorCopied
复制代码

Executors 类(不常用)

继承 ThreadPoolExecutor 类的线程池工厂类:提供 4 种工厂方法创建线程池。但该方法既不灵活也不安全,实际开发中很少使用。

// 单个线程的线程池
ExecutorService service = Executors.newSingleThreadExecutor();
// 指定数量的线程池
ExecutorService service = Executors.newFixedThreadExecutor(10); 
// 大小不限的线程池,60s 不使用会自动回收空闲线程。
ExecutorService service = Executors.newCacheThreadExecutor();
// 大小不限的线程池,可定时执行任务。
ExecutorService service = Executors.newScheduleThreadExecutor();Copy to clipboardErrorCopied
复制代码

Executors 类示例

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                System.out.println("thread id is: " + Thread.currentThread().getId());
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}



目录
相关文章
|
8天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
48 17
|
19天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
4天前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
21天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
21天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
21天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
45 3
|
21天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
116 2
|
29天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
48 6
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
2月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####