并发编程面试题1

并发编程

1、线程池中提交一个任务的流程是怎样的?

1、提交任务:首先,一个任务被提交到线程池。这个任务通常是一个实现了RunnableCallable接口的对象;
2、检测线程池状态:线程池会首先检测其运行状态。如果线程池不是RUNNING状态,任务会被直接拒绝
3、核心线程判断:如果当前工作线程数workerCount小于核心线程数corePoolSize,线程池会创建一个新的核心线程来执行提交的任务;
4、阻塞队列判断:如果工作线程数已经达到核心线程数,但线程池内的阻塞队列workQueue还未满,任务会被添加到这个阻塞队列中,等待执行。随后,空闲的核心线程会依次从队列中取出任务来执行;
5、非核心线程判断:如果阻塞队列已满,则入队失败,那么会尝试增加线程,如果当前线程池中线程数小于最大线程数maximumPoolSize, 线程池会创建一个新的非核心线程(也称为临时线程)来执行任务;
6、拒绝策略:如果阻塞队列满了,且工作线程数已达到最大线程数,线程池会根据预设的拒绝策略来处理这个任务。默认的处理方式是直接抛出一个RejectedExecutionException异常,但还有其他策略如CallerRunsPolicy(在调用者线程执行)、DiscardPolicy(任务直接丢弃,不做任何处理)和DiscardOldestPolicy(丢弃队列里最旧的那个任务,再尝试执行当前任务)等。但是这种策略有一个弊端就是任务执行的轨迹不会被记录下来。所以,我们往往需要实现自定义的拒绝策略, 通过实现RejectedExecutionHandler接口的方式

在整个过程中,线程池会优先使用核心线程来执行任务,其次是阻塞队列,最后是非核心线程。如果所有资源都已经用尽,任务会根据拒绝策略进行处理。

注意,线程池提供了两种主要的方法来执行任务:execute()submit()。其中,execute()方法用于提交不需要返回值的任务,而submit()方法用于提交一个任务并带有返回值,这个方法将返回一个Future类型对象,可以通过这个返回对象判断任务是否执行成功,并且可以通过future.get()方法来获取返回值。

流程图:

在这里插入图片描述

代码:

public void test() {// 定义线程池的参数int corePoolSize = 5; // 核心线程数int maximumPoolSize = 10; // 最大线程数long keepAliveTime = 60L; // 非核心线程的空闲存活时间TimeUnit unit = TimeUnit.SECONDS; // 时间单位BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100); // 阻塞队列ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);// 提交任务到线程池executor.execute(new Runnable() {@Overridepublic void run() {// 模拟耗时操作System.out.println("nihao");}});}
// 线程池execute()方法源码
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();// 当前线程数小于核心线程数if (workerCountOf(c) < corePoolSize) {// 新创建一个线程if (addWorker(command, true))return;c = ctl.get();}// 如果当前线程数大于核心线程数,则添加到阻塞队列中if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}// 如果阻塞队列满了,则创建非核心线程else if (!addWorker(command, false))// 当前线程数是否>=最大线程数,执行拒绝策略reject(command);}
// 创建线程源码
private boolean addWorker(Runnable firstTask, boolean core) {retry:for (;;) {int c = ctl.get();int rs = runStateOf(c);if (rs >= SHUTDOWN &&! (rs == SHUTDOWN &&firstTask == null &&! workQueue.isEmpty()))return false;for (;;) {int wc = workerCountOf(c);// 判断当前线程数是否>=最大线程数if (wc >= CAPACITY ||wc >= (core ? corePoolSize : maximumPoolSize))return false;if (compareAndIncrementWorkerCount(c))break retry;c = ctl.get();  // Re-read ctlif (runStateOf(c) != rs)continue retry;}}boolean workerStarted = false;boolean workerAdded = false;ThreadPoolExecutor.Worker w = null;try {w = new ThreadPoolExecutor.Worker(firstTask);final Thread t = w.thread;if (t != null) {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {// Recheck while holding lock.// Back out on ThreadFactory failure or if// shut down before lock acquired.int rs = runStateOf(ctl.get());if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {if (t.isAlive()) // precheck that t is startablethrow new IllegalThreadStateException();workers.add(w);int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;workerAdded = true;}} finally {mainLock.unlock();}if (workerAdded) {t.start();workerStarted = true;}}} finally {if (! workerStarted)addWorkerFailed(w);}return workerStarted;}

2、线程池有几种状态?分别是如何变化的?

线程池有五种状态,分别是:

1、Running(运行状态):
线程池经过初始化后,就进入了运行状态。这个状态下,线程池中的线程开始执行任务,并且会接受新任务、会处理队列中的任务

2、Shutdown(关闭状态):
当调用线程池的shutdown()方法后,线程池的状态会变为Shutdown。这个状态下,线程池不会接收新的任务,会处理队列中的任务,任务处理完后会中断所有线程

3、Stop(停止状态):
当调用线程池的shutdownNow()方法后,线程池的状态会变为Stop。这个状态下,线程池不会接收新的任务,不会处理队列中的任务,并且会直接中断所有线程

4、Tidying(整理状态):
所有的线程都停止了之后,线程池的状态会转变为TIDYING,一旦达到此状态,就会调用线程池的terminated()方法;

5、Terminated(终止状态):
线程池处于TIDYING状态后,会执行terminated()方法,执行完后就进入Terminated状态。

         RUNNING:  Accept new tasks and process queued tasks*   SHUTDOWN: Don't accept new tasks, but process queued tasks*   STOP:     Don't accept new tasks, don't process queued tasks,*             and interrupt in-progress tasks*   TIDYING:  All tasks have terminated, workerCount is zero,*             the thread transitioning to state TIDYING*             will run the terminated() hook method*   TERMINATED: terminated() has completed

线程池的状态变化通常是通过调用shutdown()shutdownNow()方法来实现的。当调用shutdown()方法时,线程池的状态会从RunningShutdown,再到Tidying,最后到Terminated销毁状态。当调用shutdownNow()方法时,线程池的状态会从RunningStop,再到Tidying,最后到Terminated销毁状态

代码:

// shutdown()方法源码
public void shutdown() {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();// 修改线程池状态为SHUTDOWNadvanceRunState(SHUTDOWN);// 关闭线程interruptIdleWorkers();onShutdown(); // hook for ScheduledThreadPoolExecutor} finally {mainLock.unlock();}// 当线程都关闭后,执行tryTerminate()方法,将线程池的状态修改为TIDYINGtryTerminate();}
// shutdownNow()方法源码
public List<Runnable> shutdownNow() {List<Runnable> tasks;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();// 修改线程池状态为STOP,只修改状态。先修改状态,在关闭线程,防止有新的任务进来advanceRunState(STOP);// 关闭线程interruptWorkers();tasks = drainQueue();} finally {mainLock.unlock();}// 当线程都关闭后,执行tryTerminate()方法,将线程池的状态修改为TIDYINGtryTerminate();return tasks;}
final void tryTerminate() {for (;;) {int c = ctl.get();if (isRunning(c) ||runStateAtLeast(c, TIDYING) ||(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))return;if (workerCountOf(c) != 0) { // Eligible to terminateinterruptIdleWorkers(ONLY_ONE);return;}final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {// ctlOf(TIDYING, 0) 修改线程池状态为TIDYINGif (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {try {// 空方法,当线程池都关闭后,如果想要做其他额外处理,可重写此方法进行扩展terminated();} finally {// 修改线程池状态为TERMINATED,最终状态,线程池到此真正关闭ctl.set(ctlOf(TERMINATED, 0));termination.signalAll();}return;}} finally {mainLock.unlock();}// else retry on failed CAS}}

3、如何停止一个线程?

Thread类中有两个方法:
start():开启一个线程
stop():停止一个线程
但是stop()方法不建议使用,并且是有可能在未来版本中删除掉的
在这里插入图片描述

因为stop()方法太粗暴了,一旦调用了stop()方法,就会直接停掉线程,这样就可能造成严重的问题,比如任务执行到哪一步了?锁释放了吗?等一系列问题。
注意:stop()方法会释放线程占用的synchronized锁,而不会自动释放ReentrantLock锁

public static void main(String[] args) {Object lock = new Object();Thread thread = new Thread(()->{synchronized (lock) {for (int i = 0; i < 100; i++) {System.out.println(i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});thread.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}thread.stop();synchronized (lock){// 程序能正常打印这条语句,说明线程调用stop()方法会释放synchronized锁System.out.println("拿到锁了");}}
public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Thread thread = new Thread(() -> {// 加ReentrantLock锁lock.lock();for (int i = 0; i < 100; i++) {System.out.println(i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}// 释放锁lock.unlock();});thread.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}thread.stop();lock.lock();// 程序不能正常打印这条语句,说明线程调用stop()方法不会释放ReentrantLock锁System.out.println("拿到锁了");lock.unlock();}

正常情况下我们可以使用中断机制(Interrupt)来停止一个线程

Thread类中有interrupt()方法可以来停止线程,interrupt()方法并不是直接停止线程,而是在线程中设置一个中断状态。线程可以使用isInterrupted()方法检查中断状态,并据此决定是否继续执行;

如果线程处于阻塞状态(如sleep()wait()等),调用interrupt()会唤醒线程,并抛出InterruptedException异常,同时会清除isInterrupted()方法的中断状态,重新置为false;

另外,线程池中也是通过interrupt()来停止线程的,比如shutdownNow()方法中就会调用

public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 100; i++) {// 由当前线程决定是否中断线程if (Thread.currentThread().isInterrupted() && i > 50) {break;}// 打印到50线程被就中止掉System.out.println(i);}});thread.start();// 中断线程thread.interrupt();System.out.println("end");}
public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 100; i++) {// 由当前线程决定是否中断线程if (Thread.currentThread().isInterrupted() && i > 50) {break;}System.out.println(i);try {// 当线程存在阻塞状态,会清除中止状态,这里会打印到99Thread.sleep(1000);} catch (InterruptedException e) {
//                    e.printStackTrace();}}});thread.start();// 中断线程thread.interrupt();System.out.println("end");}

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

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

相关文章

javafx使用发现的问题

1.按钮的方法 如果在fxml按钮的方法报错&#xff0c;并且你已在lei中添加了它的按钮及其按钮方法&#xff0c;那么可能是FXML和控制器类未正确关联&#xff1a; 确保你的FXML文件通过 fx:controller 属性正确指定了与之关联的控制器类。例如&#xff0c;fx:controller"c…

数据库之存储引擎

目录 一、MySQL支持的存储引擎 二、查看MySQL默认存储引擎 三、修改MySQL默认存储引擎 四、常用的存储引擎 1.InnoDB 2.MyISAM 3.MEMORY 一、MySQL支持的存储引擎 使用SHOW ENGINES \G; 命令查看 以“\G”结尾&#xff0c;其作用是将查询结果按列显示。 Engine&#xff…

更加深入Mysql-04-MySQL 多表查询与事务的操作

文章目录 多表查询内连接隐式内连接显示内连接 外连接左外连接右外连接 子查询 事务事务隔离级别 多表查询 有时我们不仅需要一个表的数据&#xff0c;数据可能关联到俩个表或者三个表&#xff0c;这时我们就要进行夺标查询了。 数据准备&#xff1a; 创建一个部门表并且插入…

Fiddler 导出请求为curl格式

来自:https://www.cnblogs.com/yudongdong/p/15418181.html Fiddler 下载地址: https://downloads.getfiddler.com/fiddler-classic/FiddlerSetup.5.0.20243.10853-latest.exe 这段代码加到类中 public static RulesOption("关闭请求体转代码", "生成代码&qu…

达梦数据库系列—29. DTS迁移ORACLE到DM

目录 1.ORACLE源端信息 2.DM目的端信息 3.DTS 迁移评估 4.数据库迁移 4.1 Oracle 源端数据库准备 4.2 目的端达梦数据库准备 初始化参数设置 兼容性参数设置 表空间规划 用户规划 创建迁移用户和表空间 4.3迁移步骤 创建迁移 配置数据源 配置迁移对象及策略 开…

django-vue-admin项目运行

文本主要对django-vue-admin项目进行了简要介绍&#xff0c;并且对前后端进行了源码安装和运行。在此基础上可作为管理系统二次开发的基础框架。 一.django-vue-admin简介和安装 1.简介 django-vue-admin项目是基于RBAC模型权限控制的中小型应用的基础开发平台&#xff0c;采…

昇思MindSpore学习总结十六 —— 基于MindSpore的GPT2文本摘要

1、mindnlp 版本要求 !pip install tokenizers0.15.0 -i https://pypi.tuna.tsinghua.edu.cn/simple # 该案例在 mindnlp 0.3.1 版本完成适配&#xff0c;如果发现案例跑不通&#xff0c;可以指定mindnlp版本&#xff0c;执行!pip install mindnlp0.3.1 !pip install mindnlp …

使用Amazon Web Services Lambda把天气预报推送到微信

最近北京开始下雨&#xff0c;开始和同事打赌几点能够雨停&#xff0c;虽然Iphone已经提供了实时天气&#xff0c;但是还是想用国内的API试试看看是不是更加准确些。 以下是我使用的服务&#xff1a; 地图SDK/APP获取 经纬度彩云天气API 通过地理位置获取天气信息Lambda 作为…

关于Mysql的面试题(实时更新中~)

一、主键约束与“not null unique”区别 1、作为Primary Key的域/域组不能为null&#xff0c;而Unique Key可以。 2、在一个表中只能有一个Primary Key&#xff0c;而多个Unique Key可以同时存在。unique not null 可以 将表的一列或多列定义为唯一性属性&#xff0c;而prima…

buu做题(6)

目录 [GWCTF 2019]我有一个数据库 [WUSTCTF2020]朴实无华 [GWCTF 2019]我有一个数据库 什么都没有, 尝试用dirsearch扫一下目录 可以扫到一个 /phpmyadmin 可以直接进入到数据库里面 但里面没什么东西 可以看到它的版本不是最新的, 搜一下相关的漏洞 phpMyAdmin 4.8.1后台文…

go关于string与[]byte再学深一点

目标&#xff1a;充分理解string与[]bytes零拷贝转换的实现 先回顾下string与[]byte的基本知识 1. string与[]byte的数据结构 reflect包中关于字符串的数据结构 // StringHeader is the runtime representation of a string.type StringHeader struct {Data uintptrLen int} …

ClickHouse 入门(一)【基本特点、数据类型与表引擎】

前言 今天开始学习 ClickHouse &#xff0c;一种 OLAP 数据库&#xff0c;实时数仓中用到的比较多&#xff1b; 1、ClickHouse 入门 ClickHouse 是俄罗斯的 Yandex&#xff08;搜索引擎公司&#xff09;在 2016 年开源的列式存储数据库&#xff08;HBase 也是列式存储&#xf…

某宝同款度盘不限速后台系统源码

简介&#xff1a; 某宝同款度盘不限速后台系统源码&#xff0c;验证已被我去除&#xff0c;两个后端系统&#xff0c;账号和卡密系统 第一步安装宝塔&#xff0c;部署卡密系统&#xff0c;需要环境php7.4 把源码丢进去&#xff0c;设置php7.4&#xff0c;和伪静态为thinkphp…

山东济南十大杰出人物起名大师颜廷利:影响世界的思想家哲学家教育家

在宇宙的广袤舞台上&#xff0c;各类智者以他们独特的方式揭示着世界的奥秘。数学家们在无尽的符号与公式中穿梭&#xff0c;像探索者般解锁着自然界的深层逻辑。考古学家们则跋涉于古老的土地&#xff0c;用他们的双手拂去岁月的尘埃&#xff0c;让沉睡的历史重见天日。 二十一…

spss是什么软件?spss有什么用

spss是什么软件&#xff1f; SPSS是一款数据统计、分析软件&#xff0c;它由IBM公司出品&#xff0c;这款软件平台提供了文本分析、大量的机器学习算法、数据分析模型、高级统计分析功能等&#xff0c;软件易学且功能非常强大&#xff0c;可以使用SPSS制作图表&#xff0c;例如…

汽车免拆诊断案例 | 2017 款林肯大陆车发动机偶尔无法起动

故障现象 一辆2017款林肯大陆车&#xff0c;搭载2.0T发动机&#xff0c;累计行驶里程约为7.5万km。车主进厂反映&#xff0c;有时按下起动按钮&#xff0c;起动机不工作&#xff0c;发动机无法起动&#xff0c;组合仪表点亮正常&#xff1b;多次按下起动按钮&#xff0c;发动机…

(21)起落架/可伸缩相机支架

文章目录 前言 1 连接到自动驾驶仪 2 通过任务规划器设置 3 其他参数 4 参数说明 前言 Copter 和 Plane 支持可伸缩的起落架/相机支架&#xff0c;由伺服机制激活&#xff08;如 Hobby King 出售的用于copters 的这些&#xff09;。齿轮/支架可以手动缩回或用一个辅助开关…

【 DHT11 温湿度传感器】使用STC89C51读取发送到串口、通过时序图编写C语言

文章目录 DHT11 温湿度传感器概述接线数据传送通讯过程时序图检测模块是否存在 代码实现总结对tmp tmp << 1;的理解对sendByte(datas[0]/10 0x30);的理解 DHT11 温湿度传感器 使用80C51单片机通过读取HDT11温湿度传感的数据&#xff0c;发送到串口。 通过时序图编写相应…

微信小程序数组绑定使用案例(一)

微信小程序数组绑定案例&#xff0c;修改数组中的值 1.Wxml 代码 <view class"list"><view class"item {{item.ischeck?active:}}" wx:for"{{list}}"><view class"title">{{item.name}} <text>({{item.id}…

Redis7(二)Redis持久化双雄

持久化之RDB RDB的持久化方式是在指定时间间隔&#xff0c;执行数据集的时间点快照。也就是在指定的时间间隔将内存中的数据集快照写入磁盘&#xff0c;也就是Snapshot内存快照&#xff0c;它恢复时再将硬盘快照文件直接读回到内存里面。 RDB保存的是dump.rdb文件。 自动触发…