Java多线程(三)——线程池及定时器

news/2024/10/31 3:21:11/

线程池就是一个可以复用线程的技术。前面三种多线程方法就是在用户发起一个线程请求就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。线程池就相当于预先创建好几个线程(招聘几个打工人),来分配之后要处理的任务(干活)

线程池的接口:ExecutorService

线程池对象

  1. 使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

可以看到有7个参数,通过这些参数设置线程池的规模和特征。ExecutorService的常用方法有execute、submit、shutdown、shutdownNow。

  1. ThreadPoolExecutor构造器的参数:

  1. 1:指定线程池的线程数量(核心线程): corePoolSize

  1. 2:指定线程池可支持的最大线程数: maximumPoolSize

  1. 3:指定临时线程的最大存活时间: keepAliveTime

  1. 4:指定存活时间的单位(秒、分、时、天): unit

  1. 5:指定任务队列: workQueue

  1. 6:指定用哪个线程工厂创建线程: threadFactory

  1. 7:指定线程忙,任务满的时候,新任务来了怎么办: handler,默认丢弃任务并抛出RejectedExecutionException异常。

  1. ThreadPoolExecutor创建线程池对象

ExecutorService pool=new ThreadPoolExecutor(3,6,8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(6), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
  1. 线程池处理Runnable任务 ——pool.execute()

首先实现Runnable接口,重写run方法。然后创建MyRunnable任务对象,只不过这里不是把MyRunnable任务对象交给Thread处理,而是使用线程池pool的execute()方法。

class myRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " ——>" + i);}try {System.out.println(Thread.currentThread().getName() + " —— 休眠");Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + " —— 启动");} catch (InterruptedException e) {e.printStackTrace();}}
}
//Runnable target=new myRunnable();pool.execute(target);
  1. 线程池处理Callable任务 ——pool.submit()

定义类实现Callable接口,重写call方法,封装要做的事情。然后把Callable对象进行submit(),并且可以返回执行后的结果。

class myCallable implements Callable<String> {private int n;public myCallable(int n) {this.n = n;}@Overridepublic String call() throws Exception {int s=0;for (int i = 0; i < n; i++) {s+=n;}return Thread.currentThread().getName()+" 1+...+" + n +", 子线程执行结果: "+s;}
}
//Callable myCallable=new myCallable(10);Future<String> f1 = pool.submit(myCallable);System.out.println(f1.get());
  1. 使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的。Executors得到线程池对象的常用方法:

方法

说明

弊端

public static ExecutorService newCachedThreadPool()

线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉。

允许创建的线程数量最大上限是Integer.MAX_VALUE,非常非常大

可能会创建大量线程,出现OOM错误( 内存溢出 java.lang.OutOfMemoryError )

public static ExecutorService newFixedThreadPool(int nThreads)

创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。

允许请求的任务队列长度是Integer.MAX_VALUE,可能会堆积大量请求,出现OOM错误

public static ExecutorService newSingleThreadExecutor ()

创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。

允许请求的任务队列长度是Integer.MAX_VALUE,可能会堆积大量请求,出现OOM错误

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。

允许创建的线程数量最大上限是Integer.MAX_VALUE,

可能会创建大量线程,出现OOM错误

Executors不适合做大型互联网场景的线程池方案,建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险。

  1. 定时器

定时器是一种控制任务延时调用,或者周期调用的技术。定时器的实现方式有两种:

方式一:Timer。创建Timer定时器对象,然后开启定时器。这是一种单线程方法,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入。可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续任务执行。

    Timer t=new Timer();t.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定时器~");}},3000,3000);

方式二: ScheduledExecutorService定时器。基于线程池,某个任务的执行情况不会影响其他定时任务的执行。先得到线程池对象,然后再进行周期调度方法。

        ScheduledExecutorService pool=Executors.newScheduledThreadPool(3);pool.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {System.out.println("定时器");}},3,3,TimeUnit.SECONDS);
  1. 并发与并行

CPU同时可以处理线程的数量有限,所以CPU会轮询为每个线程服务,只是由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,其实可能只是并发。所以说多个线程其实是并发与并行同时进行的

  1. 线程的6种状态

NEW(新建)

线程刚被创建,但是并未启动。

Runnable(可运行)

线程已经调用了start()等待CPU调度。

Blocked(锁阻塞)

线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态。

Waiting(无限等待)

一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒。

Timed Waiting(计时等待)

同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。带有超时参数的常用方法有Thread.sleep 、Object.wait。

Teminated(被终止)

因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。


http://www.ppmy.cn/news/29822.html

相关文章

YOLO-V5 系列算法和代码解析(八)—— 模型移植

文章目录工程目标芯片参数查阅官方文档基本流程Python 版工具链安装RKNPU2的编译以及使用方法移植自己训练的模型工程目标 将自己训练的目标检测模型【YOLO-V5s】移植到瑞芯微【3566】芯片平台&#xff0c;使用NPU推理&#xff0c;最终得到正确的结果。整个过程涉及模型量化、…

为啥预编译SQL能够防止SQL注入

前言之前我一个搞网络安全的朋友问了我一个的问题&#xff0c;为啥用 PreparedStatement 预编译的 SQL 就不会有被 SQL 注入的风险&#xff1f;第一时间我联想到的是八股文中关于 Mybatis 的脚本 ${} 和 #{} 的问题&#xff0c;不过再想想&#xff0c;为啥 ${} 会有 SQL 注入的…

如何使用Spring Cloud搭建高可用的Elasticsearch集群?详解Elasticsearch的安装与配置及Spring Boot集成的实现

Spring Cloud 是一个基于 Spring Boot 的微服务框架&#xff0c;它提供了一系列组件和工具&#xff0c;方便开发人员快速搭建和管理分布式系统。Elasticsearch 是一个开源的全文搜索引擎&#xff0c;也是一个分布式、高可用的 NoSQL 数据库。本篇博客将详细讲解如何使用 Spring…

I.MX6ULL_Linux_系统篇(21) kernel启动流程

链接脚本 vmlinux.lds 要分析 Linux 启动流程&#xff0c;同样需要先编译一下 Linux 源码&#xff0c;因为有很多文件是需要编译才会生成的。首先分析 Linux 内核的连接脚本文件 arch/arm/kernel/vmlinux.lds&#xff0c;通过链接脚本可以 找到 Linux 内核的第一行程序是从哪里…

HTML、CSS学习笔记5(移动端基础知识、Flex布局)

一、移动端基础知识 1.PC端和移动端区别 移动端&#xff1a;手机版网页&#xff0c;手机屏幕小&#xff0c;网页宽度多数为100%&#xff0c;没有版心 PC端&#xff1a;电脑版网页&#xff0c;屏幕大&#xff0c;网页固定版心 PC端和移动端不是同一个网页 2.如何在电脑里面…

Python写一个自动发送直播弹幕的工具,非常简单

哈喽大家好&#xff0c;今天给大家用Python整一个可以在直播间自动发弹幕的工具&#xff0c;来为喜欢的主播疯狂扣6 &#xff01; 事情原由昨晚回家&#xff0c;表弟在看LOL直播&#xff0c;看得我气不打一处来&#xff0c;差点就想锤他。 身为程序员的表弟&#xff0c;看直…

自学大数据第三天~终于轮到hadoop了

前面那几天是在找大数据的门,其实也是在搞一些linux的基本命令,现在终于轮到hadoop了 Hadoop hadoop的安装方式 单机模式: 就如字面意思,在一台机器上运行,存储是采用本地文件系统,没有采用分布式文件系统~就如我们一开始入门的时候都是从本地开始的; 伪分布式模式 存储采用…

『Javascript基础重点』1.提前声明问题,你苦恼写了js函数代码但是不会生效的问题吗?

目录1. var与声明提前不可不说的故事2. 变量的声明提前例程2.1 对于函数内外值的改变&#xff08;关键区别&#xff0c;第二点和第三点目前没有遇到&#xff09;2.2 delete删除2.3 提升变量声明3. 函数的声明提前例程总结欢迎关注 『Javascript基础重点』 专栏&#xff0c;持续…