【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 + "]";}}
}
相关文章:

【Java】一文看懂Thread 线程池的 7 种创建方式、任务队列及自定义线程池(代码示例)
本文摘要:【Java】Thread 线程池的 7 种创建方式及自定义线程池(代码示例版) 😎 作者介绍:我是程序员洲洲,一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专…...

【SpringBoot】四种读取 Spring Boot 项目中 jar 包中的 resources 目录下的文件
本文摘要:四种读取 Spring Boot 项目中 jar 包中的 resources 目录下的文件 😎 作者介绍:我是程序员洲洲,一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。公粽号…...

掌控未来,爱普生SR3225SAA用于汽车钥匙、射频电路的智慧引擎
为了响应市场需求,Epson使用独家QMEMS*2技术所生产的石英振荡器,与其精巧的半导体技术所制造的射频传输器电路,开发了SR3225SAA。不仅内建的石英震荡器之频率误差仅有2 ppm,更使其封装尺寸达仅3.2 mm x 2.5 mm,为客户大…...

第五届武汉纺织大学ACM程序设计竞赛 个人题解(待补完)
前言: 上周周日教练要求打的一场重现赛,时长五个小时,题目难度还行,除了部分题目前我还写不出来之外,大部分题都写完或补完了,这边给出比赛链接和我的代码(有些是队友的)和题解。 正…...

LeetCode---哈希表
242. 有效的字母异位词 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。 代码示例: //时间复杂度: O(n) //空间复杂度: O(1) c…...
Python知识点13---面向对象的编程
提前说一点:如果你是专注于Python开发,那么本系列知识点只是带你入个门再详细的开发点就要去看其他资料了,而如果你和作者一样只是操作其他技术的Python API那就足够了。 Python是一个完全面向对象开发的语言,它的一切都以对象的…...
Android Dialog软键盘弹出问题完美解决办法
一、问题: Dialog中有输入框时,显示后无法自动弹起软键盘,原因就不赘述了,自行Google。 一、解决办法: 开启独立线程,线程中使用while循环,循环调用弹起软键盘方法,直至showSoftI…...

【C++】C++入门1.0
鼠鼠最近在学C,那么好,俺来做笔记了! 目录 1.C关键字(C98) 2.命名空间 2.1.命名空间定义 2.2.命名空间的使用 3.C的输入&&输出 4.缺省参数 4.1缺省参数概念 4.2.缺省参数的分类 5.函数重载 5.1.函数重载概念 5.2.C支持函数…...

springboot实现文件上传功能,整合云服务
文章目录 这是springboot案例的,文件上传功能的拆分,本篇将带大家彻底了解文件上传功能,先从本地存储再到云存储,全网最详细版本,保证可以学会,可以了解文件上传功能的开发文件上传功能剖析进行书写一个小的文件上传文件上传的文件三要素首先表单提交的方式要是 post方式,第二个…...

接口interfance的基本使用
一.为什么有接口? 接口:就是一种规则。 二.接口的定义和使用 1.接口用关键字interface来定义 public interface 接口名{} 2.接口不能实例化 3.接口和类之间是实现关系,通过implements关键字表示 4.接口的子类(实现类) 注意1: 接口和类的实现关系…...
Gitlub如何删除分支(删除远程分支+本地分支)
目录 背景 删除方法 总结 背景 想要删除自己在本地创建的并已上传到远程分支中的分支。 删除方法 1)删除远程分支 git push origin --delete brannchname 2)删除本地分支 先切换到其他分支 git checkout otherbranch 删除本地分支 git bran…...

使用RSA算法加密字符串:从基础到实现 - Python
在现代数据安全中,加密算法起着至关重要的作用。特别是非对称加密算法,如RSA(Rivest-Shamir-Adleman),广泛应用于保护敏感信息的传输。本文将详细介绍如何使用RSA算法加密和解密字符串,包括生成密钥对、加密…...
MFC实现守护进程,包括开机自启动、进程单例、进程查询、进程等待、重启进程、关闭进程
在Windows平台上实现一个守护进程,由于与系统有关,所有使用MFC来实现是最合适的,被守护的进程则不限语言。废话不多,直接开整。 目录 1. 开机自启动 2. 进程单例 3. 进程查询 4. 进程等待 5. 重启进程 6. 关闭进程 7、最后…...
Spark SQL数据源 - Parquet文件
当使用Spark SQL处理Parquet文件时,你可以使用spark.read.parquet()方法从文件系统中加载Parquet数据到一个DataFrame中。Parquet是一种列式存储格式,非常适合用于大数据集,因为它提供了高效的压缩和编码方案。 以下是一个简单的例子&#x…...

eNsp——两台电脑通过一根网线直连通信
一、拓扑结构 二、电脑配置 ip和子网掩码,配置两台电脑处于同一网段 三、测试 四、应用 传文件等操作,可以在一台电脑上配置FTP服务器...

杂牌记录仪TS视频流恢复方法
大多数的记录仪都采用了MP4/MOV文件方案,极少数的可能在用AVI文件,极极少数的在用TS文件方案。很多人可能不太解TS文件,这是一种古老的视频文件结构,下边这个案例我们来看下TS视频文件的恢复方法。 故障存储:8G存储卡/fat32文件系…...

十_信号7-信号集
int sigemptyset(sigset_t *set); 清空信号集 int sigfillset(sigset_t *set); 填充满 信号集 int sigaddset(sigset_t *set, int signum); 向信号集中添加信号 int sigdelset(sigset_t *set, int signum); 从型号集中删除信号 int sigismember(const sigset_t *set, int s…...
GPT-4o
微软最新发布的CopilotPC采用了OpenAI最新的GPT-4o技术,新增了多项强大功能。以下是主要的新增功能: 更强大的AI处理能力:CopilotPC采用了专门用于AI处理的特殊芯片,使得电脑能够处理更多的人工智能任务,而无需调用云…...

32位与64位程序下函数调用的异同——计科学习中缺失的内容
前言 今天,通过一个有趣的案例,从反编译的角度看一下C语言中函数参数是如何传递的。 创建main.c文件,将下面实验代码拷贝到main.c文件中。 # main.c #include <stdio.h>int test(int a, int b, int c, int d, int e, int f, int g, …...

Python爬虫实战(实战篇)—16获取【百度热搜】数据—写入Ecel(附完整代码)
文章目录 专栏导读背景结果预览1、爬取页面分析2、通过返回数据发现适合利用lxmlxpath3、继续分析【小说榜、电影榜、电视剧榜、汽车榜、游戏榜】4、完整代码总结 专栏导读 🔥🔥本文已收录于《Python基础篇爬虫》 🉑🉑本专栏专门…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...