Java线程池

线程池

线程池:一个容纳多个线程的容器,容器中的线程可以重复使用,省去了频繁创建和销毁线程对象的操作。

线程池的作用

  1. 降低资源消耗,避免频繁的创建和销毁线程。每个工作线程都可以被重复利用,可执行多个任务。
  2. 提高响应速度,当任务到达时,如果有线程可以直接用,不会出现系统僵死。
  3. 对线程进行管理,防止无休止的创建线程而引起资源匮乏进而导致的系统崩溃,对其进行统一的分配,调优和监控。

核心思想

线程池的核心思想是线程复用,即同一个线程可以被重复使用,来处理多个任务。也就是使用池化技术 (Pool),一种编程技巧,核心思想是资源复用,在请求量大时能优化应用性能,降低系统频繁建连的资源开销。

在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停的检查是否有任务需要被执行。

⭐Java的线程池

Java 主要是通过构建ThreadPoolExecutor来创建线程池的。接下来我们看一下线程池是如何构造出来的
其构造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

从其构造方法可以看出,其参数有7个,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。下边是对每一个参数的解释:

  • corePoolSize:线程池中用来工作的核心线程数量,核心线程,即使空闲也不会被销毁。
  • maximumPoolSize:最大线程数,线程池允许创建的最大线程数,非核心线程数:maximumPoolSize-corePoolSize
  • keepAliveTime:空闲线程存活时间。一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定,只影响非核心线程
  • unit:空闲线程存活时间单位,keepAliveTime的时间单位。
  • workQueue:任务队列,是一个阻塞队列,当线程数达到核心线程数后,会将任务存储在阻塞队列中。JDK提供了许多队列,常见的如下:
    • ArrayBlockingQueue:FIFO,基于数组的有界阻塞队列,可以防止资源耗尽问题。
    • LinkedBlockingQueue:基于链表的无界阻塞队列(其实最大容量为Interger.MAX),使用该队列时,就不会创建非核心线程,容易溢出。
    • SynchronousQuene:SynchronousQueue没有容量,生产者放入一个任务,必须等待一个消费者取出,继续放入新的任务。
    • PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现
  • threadFactory :线程池内部创建线程所用的工厂,创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。
  • handler:拒绝策略;当队列已满并且线程数量达到最大线程数量时,会调用该方法处理任务。JDK内置的拒绝策略如下:
    • AbortPolicy:直接抛出异常,阻止系统正常运行。
    • CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
    • DiscardOldestPolicy:丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
    • DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这是最好的一种方案

在这里插入图片描述

![[Pasted image 20240709210100.png]]

注意的几个点

  1. keepAliveTime设置的只是非核心线程的存活时间,不影响核心线程。
  2. 当核心线程已经全部工作,此时还有任务来,就会被存储在任务队列中;如果任务队列已满,并且核心线程没有空闲,那么就会创建非核心线程(数量不超过maximumPoolSize-corePoolSize)来去处理新来的线程,而不是去任务队列中拿。

贴一个简单的线程池的实现

ThreadPool.java:

package com.qcby.threadpool;

public interface ThreadPool<T extends Runnable> {
    void execute(T job);

    void shutdown();

    void addWorks(int num);

    void removeWorks(int num);

    int getJobSize();
}

SimpleThreadPool.java

package com.qcby.threadpool;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

public class SimpleThreadPool<Job extends Runnable> implements ThreadPool<Job> {

    /**
     * 最大的工作者数量也即线程数量
     */
    private static final int MAX_WORKER_NUMBERS = 10;

    /**
     * 默认的线程数量
     */
    private static final int DEFAULT_WORKER_NUMBERS = 5;

    /**
     * 最小的线程数量
     */
    private static final int MIN_WORKER_NUMBERS = 1;

    /**
     * 任务队列
     */
    private final LinkedList<Job> jobs = new LinkedList<>();

    private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());

    /**
     * 用来标记每个线程,使用线程安全的计数器
     */
    private AtomicLong threadNum = new AtomicLong();

    /**
     * 工作者数量
     */
    private int workerNum = DEFAULT_WORKER_NUMBERS;

    public SimpleThreadPool() {
        initializeWorkers(DEFAULT_WORKER_NUMBERS);
    }

    public SimpleThreadPool(int num) {
        this.workerNum = num > MAX_WORKER_NUMBERS ? MAX_WORKER_NUMBERS : num < MIN_WORKER_NUMBERS ? MIN_WORKER_NUMBERS : num;
        initializeWorkers(workerNum);
    }

    /**
     * 初始化线程
     *
     * @param num
     */
    private void initializeWorkers(int num) {
        for (int i = 0; i < num; i++) {
            Worker worker = new Worker();
            workers.add(worker);
            Thread thread = new Thread(worker, "ThreadPool-Worker-" + threadNum.incrementAndGet());
            thread.start();
        }
    }

    /**
     * 执行任务
     *
     * @param job
     */
    @Override
    public void execute(Job job) {
        if (job != null) {
            synchronized (jobs) {
                jobs.addLast(job);
                jobs.notify();
            }
        }
    }

    /**
     * 关闭线程
     */
    @Override
    public void shutdown() {
        for (Worker worker : workers) {
            worker.shutdown();
        }
    }

    /**
     * 使用synchroized保证线程安全
     * 创建新的线程,保证总的线程数量不超过MAX_WORKER_NUMBERS
     *
     * @param num
     */
    @Override
    public void addWorks(int num) {
        synchronized (jobs) {
            if (num + workerNum > MAX_WORKER_NUMBERS) {
                num = MAX_WORKER_NUMBERS - workerNum;
            }
            initializeWorkers(num);
            workerNum += num;
        }
    }

    @Override
    public void removeWorks(int num) {
        synchronized (jobs) {
            if (num >= workerNum) {
                throw new IllegalArgumentException("beyond workNum");
            }
            int count = 0;
            while (count < num) {
                Worker worker = workers.get(count);
                if (workers.remove(worker)) {
                    worker.shutdown();
                    count++;
                }
            }
            workerNum -= count;
        }
    }

    @Override
    public int getJobSize() {
        return jobs.size();
    }

    class Worker implements Runnable {
        private volatile boolean running = true;

        @Override
        public void run() {
            while (running) {
                Job job = null;
                synchronized (jobs) {
                    while (jobs.isEmpty()) {
                        try {
                            jobs.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }
                    job = jobs.removeFirst();
                }
                if (job != null) {
                    job.run();
                }
            }
        }

        public void shutdown() {
            running = false;
        }
    }
}

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程池是一种用于管理和复用线程的机制,它可以提高多线程应用程序的性能和效率。线程池中的线程可以被重复使用,避免了频繁创建和销毁线程的开销。 在Java中,线程池可以通过`ExecutorService`接口来创建和管理。线程池中的线程可以执行提交给它的任务,并且可以根据需要自动创建新的线程或销毁闲置的线程。 嵌套线程池是指在一个线程池中创建另一个线程池。这种情况通常发生在需要处理一些复杂的任务,其中每个任务本身也需要使用线程池来执行。 下面是一个示例代码,演示了如何在Java中嵌套使用线程池: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class NestedThreadPoolExample { public static void main(String[] args) { // 创建外层线程池 ExecutorService outerThreadPool = Executors.newFixedThreadPool(5); // 提交任务给外层线程池 outerThreadPool.execute(() -> { // 创建内层线程池 ExecutorService innerThreadPool = Executors.newFixedThreadPool(3); // 提交任务给内层线程池 innerThreadPool.execute(() -> { // 内层线程池执行的任务 System.out.println("Inner thread pool task executed"); }); // 关闭内层线程池 innerThreadPool.shutdown(); }); // 关闭外层线程池 outerThreadPool.shutdown(); } } ``` 在上面的示例中,我们首先创建了一个外层线程池`outerThreadPool`,它使用`Executors.newFixedThreadPool()`方法创建了一个固定大小的线程池。然后,我们向外层线程池提交了一个任务,该任务在执行时创建了一个内层线程池`innerThreadPool`,并向内层线程池提交了一个任务。最后,我们分别关闭了内层线程池和外层线程池

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值