Java线程池的使用方法有哪些

发布时间:2023-03-24 14:29:33 作者:iii
来源:亿速云 阅读:137

Java线程池的使用方法有哪些

目录

  1. 引言
  2. 线程池的基本概念
  3. Java中的线程池
  4. 线程池的创建与配置
  5. 线程池的使用场景
  6. 线程池的监控与调优
  7. 常见问题与解决方案
  8. 线程池的扩展与自定义
  9. 线程池的关闭与销毁
  10. 线程池的最佳实践
  11. 总结

引言

在现代多核CPU的计算机系统中,多线程编程已经成为提高程序性能的重要手段。然而,直接创建和管理线程会带来诸多问题,如线程创建和销毁的开销、线程上下文切换的开销、线程数量的不可控等。为了解决这些问题,Java提供了线程池(Thread Pool)机制,通过线程池可以有效地管理线程的生命周期,减少线程创建和销毁的开销,提高系统的性能和稳定性。

本文将详细介绍Java线程池的使用方法,包括线程池的基本概念、Java中的线程池框架、线程池的创建与配置、线程池的使用场景、线程池的监控与调优、常见问题与解决方案、线程池的扩展与自定义、线程池的关闭与销毁以及线程池的最佳实践。

线程池的基本概念

什么是线程池

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

线程池的优势

  1. 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行。
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。

Java中的线程池

Executor框架

Java中的线程池是通过Executor框架来实现的。Executor框架是一个用于执行任务的框架,它将任务的提交与任务的执行分离。Executor框架的核心接口是Executor,它只有一个方法execute(Runnable command),用于执行任务。

public interface Executor {
    void execute(Runnable command);
}

Executor框架还提供了ExecutorService接口,它扩展了Executor接口,提供了更多的功能,如任务的提交、线程池的关闭等。

public interface ExecutorService extends Executor {
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

ThreadPoolExecutor

ThreadPoolExecutorExecutorService接口的一个实现类,它是Java线程池的核心类。ThreadPoolExecutor提供了丰富的配置选项,可以根据实际需求来创建不同类型的线程池。

public class ThreadPoolExecutor extends AbstractExecutorService {
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory);
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler);
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
}

Executors工厂类

Executors是一个工具类,提供了许多静态工厂方法,用于创建不同类型的线程池。Executors工厂类提供了以下几种常见的线程池:

  1. FixedThreadPool:固定大小的线程池。
  2. CachedThreadPool:可缓存的线程池。
  3. SingleThreadExecutor:单线程的线程池。
  4. ScheduledThreadPool:可调度的线程池。
public class Executors {
    public static ExecutorService newFixedThreadPool(int nThreads);
    public static ExecutorService newCachedThreadPool();
    public static ExecutorService newSingleThreadExecutor();
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
}

线程池的创建与配置

核心线程数与最大线程数

ThreadPoolExecutor的构造函数中有两个重要的参数:corePoolSizemaximumPoolSize

当提交的任务数超过corePoolSize时,线程池会创建新的线程来处理任务,直到线程数达到maximumPoolSize。如果任务数继续增加,线程池会将任务放入任务队列中等待执行。

线程存活时间

ThreadPoolExecutor的构造函数中还有一个参数keepAliveTime,它表示当线程池中的线程数超过corePoolSize时,多余的空闲线程的存活时间。如果在这个时间内没有新的任务提交,这些空闲线程将被销毁。

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);

任务队列

ThreadPoolExecutor的构造函数中有一个参数workQueue,它表示任务队列。任务队列用于存放等待执行的任务。常见的任务队列有以下几种:

  1. ArrayBlockingQueue:基于数组的有界阻塞队列。
  2. LinkedBlockingQueue:基于链表的无界阻塞队列。
  3. SynchronousQueue:不存储元素的阻塞队列,每个插入操作必须等待另一个线程的移除操作。
  4. PriorityBlockingQueue:具有优先级的无界阻塞队列。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);

拒绝策略

当线程池中的线程数达到maximumPoolSize,并且任务队列已满时,线程池会采取拒绝策略来处理新提交的任务。ThreadPoolExecutor提供了以下几种拒绝策略:

  1. AbortPolicy:直接抛出RejectedExecutionException异常。
  2. CallerRunsPolicy:由提交任务的线程来执行该任务。
  3. DiscardPolicy:直接丢弃任务,不抛出异常。
  4. DiscardOldestPolicy:丢弃任务队列中最旧的任务,然后重新提交当前任务。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler);

线程池的使用场景

CPU密集型任务

CPU密集型任务是指需要大量CPU计算的任务,如复杂的数学计算、图像处理等。对于CPU密集型任务,线程池的核心线程数应设置为CPU核心数+1,以避免过多的线程上下文切换。

int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
ExecutorService executor = new ThreadPoolExecutor(corePoolSize, corePoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

IO密集型任务

IO密集型任务是指需要大量IO操作的任务,如文件读写、网络通信等。对于IO密集型任务,线程池的核心线程数可以设置得较大,以充分利用CPU资源。

int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
ExecutorService executor = new ThreadPoolExecutor(corePoolSize, corePoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

混合型任务

混合型任务是指既有CPU计算又有IO操作的任务。对于混合型任务,线程池的核心线程数应根据任务的具体情况进行调整。

int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
ExecutorService executor = new ThreadPoolExecutor(corePoolSize, corePoolSize * 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

线程池的监控与调优

监控线程池状态

可以通过ThreadPoolExecutor提供的方法来监控线程池的状态,如当前线程数、活动线程数、完成任务数等。

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
System.out.println("当前线程数:" + executor.getPoolSize());
System.out.println("活动线程数:" + executor.getActiveCount());
System.out.println("完成任务数:" + executor.getCompletedTaskCount());

调优线程池参数

线程池的参数设置对系统性能有重要影响,应根据实际任务类型和系统资源进行调优。常见的调优方法包括:

  1. 调整核心线程数和最大线程数:根据任务类型和系统资源设置合适的核心线程数和最大线程数。
  2. 调整线程存活时间:根据任务提交的频率设置合适的线程存活时间。
  3. 选择合适的任务队列:根据任务的数量和优先级选择合适的任务队列。
  4. 选择合适的拒绝策略:根据任务的重要性和系统负载选择合适的拒绝策略。

常见问题与解决方案

线程池中的线程数过多

如果线程池中的线程数过多,会导致系统资源耗尽,甚至引发内存溢出。可以通过以下方法解决:

  1. 减少核心线程数和最大线程数:根据系统资源和任务类型减少核心线程数和最大线程数。
  2. 使用有界任务队列:使用有界任务队列限制任务的数量,避免任务队列无限增长。

线程池中的线程数过少

如果线程池中的线程数过少,会导致任务处理速度变慢,甚至引发任务堆积。可以通过以下方法解决:

  1. 增加核心线程数和最大线程数:根据系统资源和任务类型增加核心线程数和最大线程数。
  2. 使用无界任务队列:使用无界任务队列允许任务队列无限增长,避免任务堆积。

任务队列溢出

如果任务队列溢出,会导致任务被拒绝执行。可以通过以下方法解决:

  1. 增加任务队列容量:增加任务队列的容量,避免任务队列溢出。
  2. 使用合适的拒绝策略:选择合适的拒绝策略处理溢出的任务,如CallerRunsPolicyDiscardOldestPolicy

线程池的扩展与自定义

自定义线程工厂

可以通过实现ThreadFactory接口来自定义线程工厂,以控制线程的创建过程。

public class CustomThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    public CustomThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

自定义拒绝策略

可以通过实现RejectedExecutionHandler接口来自定义拒绝策略,以控制任务被拒绝时的处理方式。

public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("任务被拒绝:" + r.toString());
    }
}

自定义任务队列

可以通过实现BlockingQueue接口来自定义任务队列,以控制任务的存储和调度方式。

public class CustomBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E> {
    private final Queue<E> queue = new LinkedList<E>();
    private final int capacity;

    public CustomBlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public boolean offer(E e) {
        if (queue.size() < capacity) {
            queue.offer(e);
            return true;
        } else {
            return false;
        }
    }

    public E poll() {
        return queue.poll();
    }

    public E peek() {
        return queue.peek();
    }

    public int size() {
        return queue.size();
    }

    public Iterator<E> iterator() {
        return queue.iterator();
    }

    public void put(E e) throws InterruptedException {
        while (!offer(e)) {
            Thread.sleep(100);
        }
    }

    public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        while (!offer(e)) {
            if (nanos <= 0)
                return false;
            nanos = Thread.sleepNanos(100);
        }
        return true;
    }

    public E take() throws InterruptedException {
        while (true) {
            E e = poll();
            if (e != null)
                return e;
            Thread.sleep(100);
        }
    }

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        while (true) {
            E e = poll();
            if (e != null)
                return e;
            if (nanos <= 0)
                return null;
            nanos = Thread.sleepNanos(100);
        }
    }

    public int remainingCapacity() {
        return capacity - queue.size();
    }

    public int drainTo(Collection<? super E> c) {
        int n = 0;
        while (!queue.isEmpty()) {
            c.add(queue.poll());
            n++;
        }
        return n;
    }

    public int drainTo(Collection<? super E> c, int maxElements) {
        int n = 0;
        while (!queue.isEmpty() && n < maxElements) {
            c.add(queue.poll());
            n++;
        }
        return n;
    }
}

线程池的关闭与销毁

shutdown()方法

shutdown()方法用于平缓地关闭线程池,线程池不再接受新任务,但会继续执行已提交的任务。

executor.shutdown();

shutdownNow()方法

shutdownNow()方法用于立即关闭线程池,线程池会尝试停止所有正在执行的任务,并返回等待执行的任务列表。

List<Runnable> tasks = executor.shutdownNow();

awaitTermination()方法

awaitTermination()方法用于等待线程池中的任务执行完毕,或者等待指定的超时时间。

executor.awaitTermination(10, TimeUnit.SECONDS);

线程池的最佳实践

避免创建过多的线程池

过多的线程池会消耗大量的系统资源,导致系统性能下降。应尽量复用线程池,避免为每个任务创建新的线程池。

合理设置线程池参数

线程池的参数设置对系统性能有重要影响,应根据实际任务类型和系统资源进行合理设置。

使用合适的拒绝策略

选择合适的拒绝策略可以避免任务被拒绝时引发的问题,如任务丢失或系统崩溃。

总结

Java线程池是多线程编程中的重要工具,通过线程池可以有效地管理线程的生命周期,减少线程创建和销毁的开销,提高系统的性能和稳定性。本文详细介绍了Java线程池的使用方法,包括线程池的基本概念、Java中的线程池框架、线程池的创建与配置、线程池的使用场景、线程池的监控与调优、常见问题与解决方案、线程池的扩展与自定义、线程池的关闭与销毁以及线程池的最佳

推荐阅读:
  1. java中整体MR工作机制是怎样的
  2. 如何使用Java中的Serverless

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:Remix怎么集成antd和pro-components

下一篇:SpringBoot怎么整合chatGPT

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》