【Java】一文看懂Thread 线程池的 7 种创建方式、任务队列及自定义线程池(代码示例)

本文摘要:【Java】Thread 线程池的 7 种创建方式及自定义线程池(代码示例版)

在这里插入图片描述

😎 作者介绍:我是程序员洲洲,一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。公粽号:洲与AI。
🤓 欢迎大家关注!我将分享Web前后端开发、人工智能、机器学习、深度学习从0到1系列文章。
🌼 同时洲洲已经建立了程序员技术交流群,如果您感兴趣,可以私信我加入我的社群~社群中将不定时分享各类福利
🖥 随时欢迎您跟我沟通,一起交流,一起成长、进步!点此即可获得联系方式~

本文目录

  • 前言
  • 1.线程池介绍
  • 2.线程池创建的方式
    • 2.1 ThreadPoolExecutor的详细配置
    • 2.2 Executors提供的快捷创建方法
  • 3.线程池使用场景
  • 4.最佳代码实践
    • 4.1 ThreadPoolExecutor
    • 4.2 FixedThreadPool
    • 4.3 CachedThreadPool
    • 4.4 SingleThreadExecutor
    • 4.5 ScheduledThread
    • 4.6 SingleThreadScheduledExecutor
    • 4.7 NewWorkStealingPool
  • 5.任务队列
    • 5.1 直接提交队列(SynchronousQueue)
    • 5.2 有界任务队列(ArrayBlockingQueue)
    • 5.3 无界任务队列(LinkedBlockingQueue)
  • 5.4 优先任务队列(PriorityBlockingQueue)
  • 6.线程拒绝策略

前言

Java线程池是提高应用性能的关键组件。线程池通过预先创建并管理一组线程,可以显著减少因频繁创建和销毁线程而产生的资源消耗。本文将探讨Java线程池的基本概念、创建方法以及最佳实践。

需要注意的是,【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

一般情况下,推荐使用 ThreadPoolExecutor 的方式进行线程池的创建,因为这种创建方式更可控,并且更加明确了线程池的运行规则,可以规避一些未知的风险。

1.线程池介绍

线程池(ThreadPool)是一种资源管理策略,它通过复用线程来降低资源消耗、提高响应速度,并增强线程管理的可操作性。线程池预分配一定数量的线程,当任务到来时,线程池会分配现有线程去执行任务,而不是每次都创建新的线程。

线程池的优点如下:
资源节约:通过复用线程,减少了线程创建和销毁的开销。
性能提升:任务可以快速启动,因为线程已经存在。
管理增强:线程池提供了更多的控制,如线程数量、任务队列等。

2.线程池创建的方式

Java中创建线程池主要有两大类方法:

使用ThreadPoolExecutor直接创建:提供了最大的灵活性和控制力。
使用Executors工厂方法创建:提供了多种快捷方式来创建常见的线程池类型。

2.1 ThreadPoolExecutor的详细配置

ThreadPoolExecutor是最灵活的线程池创建方式,允许开发者自定义线程池的各项参数:

核心线程数:线程池中始终存活的线程数。
最大线程数:线程池中允许的最大线程数。
存活时间:非核心线程在没有任务执行时的存活时间。
时间单位:与存活时间配合使用的时间单位。
工作队列:存储等待执行任务的阻塞队列。
线程工厂:用于创建新线程的工厂。
拒绝策略:当任务太多无法处理时的策略。

2.2 Executors提供的快捷创建方法

Executors类提供了一些快捷方法来创建特定类型的线程池:

FixedThreadPool:固定大小的线程池。
CachedThreadPool:可缓存的线程池,会根据需要创建新线程。
SingleThreadExecutor:单个线程的线程池,保证任务顺序执行。
ScheduledThreadPool:可以执行定时任务的线程池。
WorkStealingPool:JDK 1.8新增,任务被多个线程池线程抢占执行。
在这里插入图片描述

3.线程池使用场景

FixedThreadPool适用于需要固定数量线程执行任务的场景。
CachedThreadPool适合处理大量短期异步任务。
SingleThreadExecutor保证任务按照提交的顺序执行。
ScheduledThreadPool适合需要定时或周期性执行任务的场景。

4.最佳代码实践

4.1 ThreadPoolExecutor

public class ThreadPoolExecutorTest {public static void main(String[] args) {ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));// 执行任务for (int i = 0; i < 10; i++) {final int index = i;threadPool.execute(() -> {System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}});}}
}

可以设置7个参数

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {}
  • corePoolSize:核心线程数,线程池中始终存活的线程数。
  • maximumPoolSize:最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。
  • keepAliveTime:最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。
  • unit:单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间。

TimeUnit.DAYS:天
TimeUnit.HOURS:小时
TimeUnit.MINUTES:分
TimeUnit.SECONDS:秒
TimeUnit.MILLISECONDS:毫秒
TimeUnit.MICROSECONDS:微妙
TimeUnit.NANOSECONDS:纳秒

  • workQueue:一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全。它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种

ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
较常用的是 LinkedBlockingQueue 和 Synchronous,线程池的排队策略与 BlockingQueue 有关

  • threadFactory:线程工厂,主要用来创建线程。
  • handler:拒绝策略,拒绝处理任务时的策略,系统提供了 4 种。

AbortPolicy:拒绝并抛出异常。(默认策略)
CallerRunsPolicy:使用当前调用的线程来执行此任务。
DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
DiscardPolicy:忽略并抛弃当前任务。

ThreadPoolExecutor 关键节点的执行流程如下:

1、当线程数小于核心线程数时,创建线程。
2、当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
3、当线程数大于等于核心线程数,且任务队列已满:若线程数小于最大线程数,创建线程;若线程数等于最大线程数,抛出异常,拒绝任务。
在这里插入图片描述

4.2 FixedThreadPool

FixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。

一般用于Web 服务瞬时削峰,但需注意长时间持续高峰情况造成的队列阻塞。

public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
  • corePoolSize 与 maximumPoolSize 相等,即其线程全为核心线程,是一个固定大小的线程池,是其优势。
  • keepAliveTime = 0 该参数默认对核心线程无效,而 FixedThreadPool 全部为核心线程。
  • workQueue 为 LinkedBlockingQueue(无界阻塞队列),队列最大值为 Integer.MAX_VALUE。如果任务提交速度持续大于任务处理速度,会造成队列大量阻塞。因为队列很大,很有可能在拒绝策略前,内存溢出。是其劣势
  • FixedThreadPool 的任务执行是无序的。
public class NewFixedThreadPoolTest {public static void main(String[] args) {System.out.println("主线程启动");// 1.创建1个有2个线程的线程池ExecutorService threadPool = Executors.newFixedThreadPool(2);Runnable runnable = new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}System.out.println("任务被执行,线程:" + Thread.currentThread().getName());}};// 2.线程池执行任务(添加4个任务,每次执行2个任务,得执行两次)threadPool.submit(runnable);threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);System.out.println("主线程结束");}
}

线程池通过复用一组固定数量的线程来执行多个任务,这些线程在一个共享的无界队列上操作。在任一时刻,至多有 nThreads 个线程在积极地处理任务。如果所有线程都忙碌且此时有新任务提交,那么这些新任务将被放入队列中排队,直到有线程空闲出来。

该线程池能够同时处理两个任务,因为有两个活跃的线程。如果这两名线程都在执行任务,那么新提交的两个任务将进入等待队列,直到这两个线程中的任何一个完成其当前任务。

在Java的线程池中,submit() 和 execute() 是两种不同的方法,它们都用于向线程池提交任务。submit() 方法允许你提交一个任务,并返回一个 Future 对象,这个对象可以用来查询任务状态、取消任务或获取任务执行结果。相比之下,execute() 方法用于提交一个任务以供执行,但它不返回任何表示任务的 Future 对象。

4.3 CachedThreadPool

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class CachedThreadPoolExample {public static void main(String[] args) {// 创建 CachedThreadPool 线程池ExecutorService threadPool = Executors.newCachedThreadPool();// 提交任务到线程池for (int i = 0; i < 10; i++) {final int taskNumber = i;threadPool.execute(() -> {System.out.println("执行任务 " + taskNumber + " 由线程 " + Thread.currentThread().getName() + " 处理");try {TimeUnit.SECONDS.sleep(1); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池,不再接受新任务threadPool.shutdown();}
}
  • CachedThreadPool 是通过Executors.newCachedThreadPool()
    方法创建的,它是一种可扩展的线程池,核心线程数为 0,最大线程数为 Integer.MAX_VALUE。
  • 该线程池适合用于执行大量短期异步任务。它在需要时会动态创建新线程,如果线程空闲时间超过 60 秒,则会被终止并从线程池中移除。
  • CachedThreadPool 使用的是一个同步队列 SynchronousQueue
    作为工作队列,这个队列没有容量,即它不会存储提交的任务,而是直接将任务交给线程执行。
  • 在示例代码中,我们创建了一个 CachedThreadPool 并提交了 10
    个任务。每个任务简单地打印出它正在被哪个线程执行,并模拟执行时间。
  • 最后,我们调用 shutdown()
    方法来关闭线程池,使其不再接受新任务。注意,这不会立即停止所有正在执行的任务,而是等待它们完成后线程池才会完全关闭。

使用 CachedThreadPool 时需要注意,由于其最大线程数可以非常大,如果任务提交得非常快,可能会导致创建大量线程,从而耗尽系统资源。因此,应当谨慎使用,并确保任务执行不会过快,或者考虑设置适当的线程池参数。

4.4 SingleThreadExecutor

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class SingleThreadExecutorExample {public static void main(String[] args) {// 创建 SingleThreadExecutor 线程池ExecutorService threadPool = Executors.newSingleThreadExecutor();// 提交多个任务到单线程线程池for (int i = 1; i <= 5; i++) {final int taskNumber = i;threadPool.execute(() -> {System.out.println("执行任务 " + taskNumber + " 由线程 " + Thread.currentThread().getName() + " 处理");try {TimeUnit.SECONDS.sleep(2); // 模拟耗时任务} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 重新设置中断状态System.out.println("任务 " + taskNumber + " 被中断");}});}// 优雅关闭线程池,等待所有任务执行完毕threadPool.shutdown();try {// 等待线程池关闭,或者超时if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {// 超时后强制关闭线程池threadPool.shutdownNow();}} catch (InterruptedException e) {threadPool.shutdownNow(); // 捕获中断异常时立即关闭线程池Thread.currentThread().interrupt(); // 重新设置中断状态}}
}

SingleThreadExecutor 是通过 Executors.newSingleThreadExecutor() 方法创建的,它确保所有的任务都在同一个线程中按顺序执行。

这种类型的线程池内部其实只有一个线程在工作,也就是单线程环境,它保证了任务的执行顺序,即先提交的任务先执行。

在示例代码中,我们创建了一个 SingleThreadExecutor 并提交了 5 个任务。每个任务简单地打印出它正在被哪个线程执行,并模拟执行时间。

我们使用了 shutdown() 方法来开始关闭线程池的过程,这将阻止线程池接受新任务,但会等待已提交的任务执行完毕。

awaitTermination 方法用来等待线程池中的所有任务执行完成,或者直到超时。如果超时,则会调用 shutdownNow() 方法尝试立即停止所有正在执行的任务。
SingleThreadExecutor 适用于任务不需要并发执行,并且希望按照特定顺序执行的场景。

4.5 ScheduledThread

public class ScheduledThreadTest {public static void main(String[] args) {ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);System.out.println("添加任务,时间:" + new Date());threadPool.schedule(() -> {System.out.println("任务被执行,时间:" + new Date());}, 2, TimeUnit.SECONDS);}
}

4.6 SingleThreadScheduledExecutor

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class SingleThreadScheduledExecutorExample {public static void main(String[] args) {// 创建 SingleThreadScheduledExecutor 线程池ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();// 提交一个任务,延迟2秒后执行scheduledExecutorService.schedule(() -> {System.out.println("任务在 " + TimeUnit.SECONDS.convert(System.nanoTime(), TimeUnit.NANOSECONDS) + " 秒后执行");}, 2, TimeUnit.SECONDS);// 重复执行的任务,每隔2秒执行一次,初始延迟2秒scheduledExecutorService.scheduleAtFixedRate(() -> {System.out.println("周期性任务执行,时间:" + new java.util.Date());}, 2, 2, TimeUnit.SECONDS);// 线程池不会立即关闭,因为任务还在执行// 可以安排关闭线程池的逻辑,例如使用 ScheduledExecutorService 的 shutdown 方法}
}

SingleThreadScheduledExecutor 是通过 Executors.newSingleThreadScheduledExecutor() 方法创建的,它是一个单线程执行定时任务的线程池。

与SingleThreadExecutor不同,SingleThreadScheduledExecutor支持定时任务和周期性任务的执行。

在示例代码中,我们首先使用schedule方法提交了一个延迟2秒后执行的单次任务。

然后,我们使用scheduleAtFixedRate方法提交了一个周期性任务,该任务每隔2秒执行一次,并且有一个初始延迟2秒。

SingleThreadScheduledExecutor 保证所有任务都在同一个线程中顺序执行,这对于需要保证任务执行顺序的场景非常有用。

由于SingleThreadScheduledExecutor是为定时任务设计的,所以它不会像shutdown方法那样立即关闭线程池。如果需要关闭线程池,应该在所有任务执行完毕后调用shutdown方法,并妥善处理关闭逻辑。

此类型的线程池适用于执行定时任务和周期性任务,如定期的数据备份、定时检查等场景。

4.7 NewWorkStealingPool

NewWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定),任务的执行顺序是不确定的,注意此方法只有在 JDK 1.8+ 版本中才能使用。

public class NewWorkStealingPoolTest {public static void main(String[] args) {ExecutorService threadPool = Executors.newWorkStealingPool();for (int i = 0; i < 10; i++) {final int index = i;threadPool.execute(() -> {System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());});}// 确保任务执行完成while (!threadPool.isTerminated()) {}}
}

5.任务队列

5.1 直接提交队列(SynchronousQueue)

直接提交队列不存储任务,每个提交的任务必须立即由线程池中的某个线程接收并开始执行。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;public class DirectSubmissionQueueExample {public static void main(String[] args) {// 使用直接提交队列SynchronousQueue<Runnable> queue = new SynchronousQueue<>();ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, queue);// 提交任务executor.execute(() -> {System.out.println("任务被执行,线程:" + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});// 关闭线程池executor.shutdown();}
}

在这个示例中,我们创建了一个核心线程数和最大线程数都为1的ThreadPoolExecutor,使用了SynchronousQueue作为任务队列。任务一提交就会尝试执行,因为没有存储机制,所以任务不会被缓存。

5.2 有界任务队列(ArrayBlockingQueue)

有界任务队列可以存储有限数量的任务。当队列满了之后,根据拒绝策略处理新提交的任务。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;public class BoundedQueueExample {public static void main(String[] args) {// 创建有界任务队列ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 0, TimeUnit.MILLISECONDS, queue);// 提交任务for (int i = 0; i < 3; i++) {executor.execute(() -> {System.out.println("任务 " + (i + 1) + " 被执行,线程:" + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}

在这个示例中,我们创建了一个有界队列ArrayBlockingQueue,容量为2。这意味着它最多可以存储两个任务。当提交的任务超过这个数量时,根据设置的拒绝策略处理。

5.3 无界任务队列(LinkedBlockingQueue)

无界任务队列可以存储无限数量的任务,直到内存限制。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;public class UnboundedQueueExample {public static void main(String[] args) {// 创建无界任务队列LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 0, TimeUnit.MILLISECONDS, queue);// 提交任务for (int i = 0; i < 10; i++) {executor.execute(() -> {System.out.println("任务 " + (i + 1) + " 被执行,线程:" + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}

在这个示例中,我们创建了一个无界队列LinkedBlockingQueue。这意味着它可以存储任意数量的任务,直到系统内存耗尽。

5.4 优先任务队列(PriorityBlockingQueue)

优先任务队列可以根据任务的优先级来执行任务。

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;public class PriorityQueueExample {public static void main(String[] args) {// 创建优先任务队列PriorityBlockingQueue<Runnable> queue = new PriorityBlockingQueue<>();ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 0, TimeUnit.MILLISECONDS, queue);// 提交任务,可以定义任务优先级executor.execute(new PrioritizedTask(5, "任务5"));executor.execute(new PrioritizedTask(1, "任务1"));executor.execute(new PrioritizedTask(10, "任务10"));// 关闭线程池executor.shutdown();}static class PrioritizedTask implements Runnable, Comparable<PrioritizedTask> {private int priority;private String taskName;public PrioritizedTask(int priority, String taskName) {this.priority = priority;this.taskName = taskName;}@Overridepublic void run() {System.out.println(taskName + " 被执行,线程:" + Thread.currentThread().getName());}@Overridepublic int compareTo(PrioritizedTask other) {return Integer.compare(this.priority, other.priority); // 升序排序}}
}

在这个示例中,我们创建了一个PriorityBlockingQueue,它根据任务的优先级来排序任务。我们定义了一个PrioritizedTask类,实现了Runnable和Comparable接口,以支持优先级排序。任务将根据它们的优先级被执行。

6.线程拒绝策略

我们来演示一下 ThreadPoolExecutor 的拒绝策略的触发,我们使用 DiscardPolicy 的拒绝策略,它会忽略并抛弃当前任务的策略,实现代码如下:

public class ThreadPoolStrategyTest {public static void main(String[] args) {// 线程池ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 100,TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy());// 任务Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("当前任务被执行,执行时间:" + new Date() +" 执行线程:" + Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}}};// 开启4个任务threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);}
}

除了 Java 自身提供的 4 种拒绝策略之外,我们也可以自定义拒绝策略,示例代码如下:

public class MyThreadPoolStrategyTest {public static void main(String[] args) {Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("当前任务被执行,执行时间:" + new Date() +" 执行线程:" + Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}}};ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 100,TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// 执行自定义拒绝策略的相关操作System.out.println("我是自定义拒绝策略~");}});threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);}
}

以下是自定义线程池,使用了有界队列,自定义 ThreadFactory 和拒绝策略的demo:

public class MyThreadPoolTest {public static void main(String[] args) throws Exception {BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);NameThreadFactory threadFactory = new NameThreadFactory();RejectedExecutionHandler handler = new MyIgnorePolicy();ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 100, TimeUnit.SECONDS,workQueue, threadFactory, handler);// 预启动所有核心线程executor.prestartAllCoreThreads();for (int i = 1; i <= 10; i++) {MyTask task = new MyTask(String.valueOf(i));executor.execute(task);}//阻塞主线程System.in.read();}static class NameThreadFactory implements ThreadFactory {private final AtomicInteger mThreadNum = new AtomicInteger(1);@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());System.out.println(t.getName() + " has been created");return t;}}static class MyIgnorePolicy implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {doLog(r, executor);}private void doLog(Runnable r, ThreadPoolExecutor e) {// 可做日志记录等System.err.println( r.toString() + " rejected");System.out.println("completedTaskCount: " + e.getCompletedTaskCount());}}static class MyTask implements Runnable {private String name;public MyTask(String name) {this.name = name;}@Overridepublic void run() {try {System.out.println(this.toString() + " is running!");// 让任务执行慢点Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}public String getName() {return name;}@Overridepublic String toString() {return "MyTask [name=" + name + "]";}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/846404.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

彩灯控制器设计 74ls160+ne555实现

一、选题背景 数字电子技术在我们生活中的应用非常之广泛,不论是在各个方面都会涉及到它,小到家用电器的自动控制,大到神舟九号和天空一号航天器的设计,都无可避免的要运用它。并且鉴于以理论推动实践及理论实践相结合为指导思想,特此用我们所学的理论知识来实践这次课程设…

【云原生】Docker Compose 使用详解

目录 一、前言 二、Docker Compose 介绍 2.1 Docker Compose概述 2.2 Docker Compose特点 2.3 Docker Compose使用场景 三、Docker Compose 搭建 3.1 安装docker环境 3.2 Docker Compose安装方式一 3.2.1 下载最新版/如果不是最新可替换最新版本 3.2.2 设置权限 3.2.…

Q-Learning 简介:初学者教程(1)

一、说明 强化学习强调无模型学习算法&#xff0c;因此出现Q-Learning&#xff0c;Q-Learning算法酷似“有限状态自动机”模型&#xff0c;只是增加了奖励机制和Agent机制&#xff0c;而Agent与粒子群算法、蒙特卡洛算法是有关的。本文介绍这个算法框架。 &#xff0c; 二、QL框…

C语言—深入理解指针(5)

1. sizeof 和 strlen 的对比 1.1 sizeof 在学习操作符的时候&#xff0c;我们学习了 sizeof&#xff0c;sizeof 是计算变量所占内存空间大小的&#xff0c;单位是字节&#xff0c;如果操作数是类型的话&#xff0c;计算的是使用类型创建的变量所占内存空间的大小。 sizeof 只…

西瓜播放器xgplayer设置自动播放踩坑

上图是官网&#xff08;西瓜视频播放器官方中文文档&#xff09;的介绍&#xff0c;相信大家都是按照官网配置去做的&#xff0c;但是并没有什么用&#xff0c;插件很好用&#xff0c;但是属性不全&#xff0c;真的很悔恨&#xff0c;找遍 api 都没有找到自动播放的属性&#x…

MongoDB-4.2.1 之安装和使用

安装 下载安装包 我自己电脑是 Windows7 的老古董&#xff0c;所以就下载老版本的 MongoDB。 mongodb: https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2012plus-4.2.1.zip 解压安装包到指定路径 我解压到的 C 盘 C:\mongodb-4.2.1 添加环境变量 创建数据库和…

【免费Web系列】JavaWeb实战项目案例六

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 员工信息-删除&修改 前面我们已经实现了员工信息的条件分页查询以及新增操作。 关于员工管理的功能&#xff0c;还有两个需要实现&#xff1a; 删除员工 修改员工 除了员工管理的功能之外&#x…

基于springboot+vue的家乡特色推荐系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

【Linux】Linux工具——gcc/g++

1.使用vim更改信用名单——sudo 我们这里来补充sudo的相关知识——添加信任白名单用户 使用sudo就必须将使用sudo的那个账号添加到信用名单里&#xff0c;而且啊&#xff0c;只有超级管理员才可以添加 信用名单在/etc/sudoers里 我们发现它的权限只是可读啊&#xff0c;所以…

分享:重庆耶非凡科技有限公司人力资源项目靠不靠谱?

在当今快速变化的商业环境中&#xff0c;人力资源项目作为企业发展的重要支撑&#xff0c;其专业性和可靠性成为企业选择合作伙伴时的重要考量因素。重庆耶非凡科技有限公司作为一家在行业内颇具影响力的科技企业&#xff0c;其人力资源项目——人力RPO(招聘流程外包)项目&…

dm8 什么时候视图中统计的内存会超过OS

v$bufferpool和v$mem_pool视图记录着DMSERVER各组件的内存占用量。理论上跟OS看到的保持一致。但实际大多数场景下&#xff0c;OS中看到的数据远大于视图中的统计。这里面可能有内存泄漏的原因。不过也有的时候视图中的统计数据超过OS。下面就是这种情况&#xff1a; 上图中红线…

Java_Mybatis

Mybatis是一款优秀的持久层框架&#xff0c;用户简化JDBC(使用Java语言操作关系型数据库的一套API)开发 使用Mybatis查询所有用户数据&#xff1a; 代码演示&#xff1a; UserMapper&#xff1a; Mapper //被调用时会通过动态代理自动创建实体类&#xff0c;并放入IOC容器中…

用cloudflared 把家里电脑网站可以让任何人试用

一针见血说技术&#xff0c;通俗易懂去实现自己的想法 一、背景 搭建一个网站&#xff0c;或者有个自己开发的算法&#xff0c;需要供应少量的人免费服务&#xff0c;也可以用于向合伙人演示。需要一个云服务&#xff0c;要么购买。还得啰嗦学习一些网站的开通知识&#xff0…

单号日入50+,全自动挂机赚钱

大家好&#xff01;今天我为大家精心挑选了一个极具潜力的副业项目——“游戏工作室自由之刃2&#xff1a;单号日入50&#xff0c;全自动挂机赚钱”。 传奇游戏&#xff0c;无疑是许多人心中那段青春时光的珍贵回忆。 即便是其手游版本&#xff0c;也依旧保持着极高的热度和人…

【教程】20个高级 Python 函数,让你编程更高效

在Python的编程世界中,函数是我们编写代码的重要工具之一。除了常见的内置函数外,Python还提供了许多强大而有趣的高级函数,它们可以帮助我们简化代码、提升效率,甚至在某些情况下让编程变得更加有趣。让我们一起来探索这些高级函数的奇妙之处吧! 1.enumerate() – 枚举函…

Chrome DevTools

Console 面板 此章节请打开 justwe7.github.io/devtools/console/console.html 一起食用 一方面用来记录页面在执行过程中的信息&#xff08;一般通过各种 console 语句来实现&#xff09;&#xff0c;另一方面用来当做 shell 窗口来执行脚本以及与页面文档、DevTools 等进行交…

【C++进阶】深入STL之string:模拟实现走进C++字符串的世界

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C模板入门 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀STL之string &#x1f4d2;1. string…

VRRP

文章目录 VRRP基本原理技术背景VRRP作用VRRP概述VRRP名词解释VRRP路由器VRRP组虚拟路由器虚拟IP地址、MAC地址Master、Backup路由器 VRRP状态机Master/ Backup 路由器Master路由器:Backup路由器: VRRP的工作过程 VRRP基础配置![image.png](https://img-blog.csdnimg.cn/img_con…

05C零碎语法

C零碎语法 目录 文章目录 C零碎语法1.函数指针2.回调函数3.数据拷贝3.1静态内存分配![请添加图片描述](https://img-blog.csdnimg.cn/direct/54d44e32bb7944f0866d4ca1e2667ce8.png)### 4.1动态内存分配 字符串6.sizeof()和strlen()的区别7.strcpy()/strncpy()函数7.1**strcp…

中继器、集线器、网桥、交换机、路由器和网关

目录 前言一、中继器、集线器1.1 中继器1.2 集线器 二、网桥、交换机2.1 网桥2.2 交换机 三、路由器、网关3.1 路由器3.2 网关 总结 前言 理解这些设备的关键是他们运行在不同的层次上。之所以存在不同层的问题&#xff0c;是因为不同的设备使用不同的信息来决定如何交换。在典…