面试之多线程案例(四)

news2024/9/21 19:36:17

1.单例模式

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

1.1单例模式的类型

单例模式包含两种类型:

  • 饿汉式:在类加载的时候已经创建好对象,等待被使用
  • 懒汉式:在真正需要使用对象的时候才去创建对象

1.2饿汉式创建单例对象

饿汉式在类加载的时候已经创建好该对象,在程序调用的时候直接返回该对象即可,不需要等到使用时再创建。

public class Singleton{
    
    private static final Singleton singleton = new Singleton();
    //此时已经实例化好了一个对象,内存中已经存在了,因此不会再存在多个Singleton对象了
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        return singleton;
    }
}

1.3懒汉式创建单例对象

image.png

懒汉式创建单例对象就是在使用单例对象之前先判断是否已经被实例化了,如果已经被实例化,则可以直接使用,否则才开始实例化。

public class Singleton {
    
    private static Singleton singleton;
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
    
}

这就是一个懒汉式创建单例对象,此时已经可以进行使用。但是还是存在一些问题。比如并发操作时,两个线程同时判断该对象为空,那么两个线程都会实例化对象,所以就会创建两个对象,已经不满足单例模式。

image.png

此时的解决办法就是在方法上加锁或者对类对象加锁,如下:

public static synchronized Singleton getInstance() {
    if (singleton == null) {
        singleton = new Singleton();
    }
    return singleton;
}
// 或者
public static Singleton getInstance() {
    synchronized(Singleton.class) {   
        if (singleton == null) {
            singleton = new Singleton();
        }
    }
    return singleton;
}

此时这样加锁就规避了两个线程同时创建对象,当一个线程获取锁时,另一个线程需要阻塞等待,保证从始至终只创建一个对象。但是这样的话,在每次获取对象时候都需要获取锁,并发性能较差。

所以我们需要优化:首先判断对象是不是为空,如果为空才获取锁进行对象的实例化,如果首次判断不为空,那么直接可以使用对象,不用再获取锁。

所以直接在方法上加锁的方式不可取,因为无论如何每次都要获取锁

public static Singleton getInstance() {
    if (singleton == null) {  // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
        synchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化
            if (singleton == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

 1.4加入volatile防止指令重排序

创建一个对象,在JVM中会经过三步:

(1)为singleton分配内存空间

(2)初始化singleton对象

(3)将singleton指向分配好的内存空间

指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能

在这三步中,第2、3步有可能会发生指令重排现象,创建对象的顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常。文字较为晦涩,可以看流程图:image.png

使用volatile关键字可以防止指令重排序,volatile可以保证指令执行顺序与程序指明顺序一致,不会发生改变。

public class Singleton {
    
    private static volatile Singleton singleton;
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        if (singleton == null) {  // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
            synchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化
                if (singleton == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
    
}

2.线程池

线程池顾名思义,就是一次创建多条线程,放在一个池子里,用的时候拿出来一个,用完之后放回去。

在实际业务中需要用到许多线程,虽然创建线程相比于创建进程来说比较轻量级,但是频繁的创建销毁也会消耗很多的资源。线程池最大的好处就是减少每次启动,销毁线程的损耗。 

2.1jdk中默认线程池

  public static void main(String[] args) {
        // 1. 用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将收回并移出缓存
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        // 2. 创建一个操作无界队列且固定大小线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        // 3. 创建一个操作无界队列且只有一个工作线程的线程池
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        // 4. 创建一个单线程执行器,可以在给定时间后执行或定期执行。
        ScheduledExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
        // 5. 创建一个指定大小的线程池,可以在给定时间后执行或定期执行。
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        // 6. 创建一个指定大小(不传入参数,为当前机器CPU核心数)的线程池,并行地处理任务,不保证处理顺序
        Executors.newWorkStealingPool();
    }

2.2创建系统自带的线程池

ThreadPoolExecutor  threadPoolExecutor = new ThreadPoolExecutor(
                5,//核心线程数
                10,//最大线程数
                1,//临时线程存活的时间
                TimeUnit.SECONDS,//时间单位
                new LinkedBlockingQueue<>(20),//阻塞队列类型
                };

2.3线程池的工作流程

2.4拒绝策略

  1. ThreadPoolExecutor.AbortPolicy,这个策略是直接拒绝,也是默认的策略

  2. ThreadPoolExecutor.CallerRunsPolicy,将任务返回给调用者(调用的线程)

  3. ThreadPoolExecutor.DiscardOldestPolicy,放弃最早等待的任务

  4. ThreadPoolExecutor.DiscardPolicy,放弃最新的任务

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

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

相关文章

SSL原理详解

SSL协议结构&#xff1a; SSL协议分为两层&#xff0c;下层为SSL记录协议&#xff0c;上层为SSL握手协议、SSL密码变化协议和SSL警告协议。 1.下层为SSL记录协议&#xff0c;主要作用是为高层协议提供基本的安全服务 建立在可靠的传输之上&#xff0c;负责对上层的数据进行分块…

HDFS集群黑白名单机制

HDFS集群黑白名单机制 白名单黑名单 白名单 所谓白名单指的是允许哪些机器加入到当前的HDFS集群中&#xff0c;是一种准入机制白名单由dfs.hosts参数指定&#xff0c;该参数位于hdfs-site.xml.默认值为空dfs.hosts只想文件&#xff0c;该文件包含允许链接到namanode的主机列表…

OpenCVForUnity(九)图片模糊

文章目录 前言一、归一化框滤波器使用blur方法来实现 二、高斯滤波器使用GaussianBlur方法实现 三、中值滤波器使用medianBlur方法实现 四、双边过滤器使用bilateralFilter方法实现 结语 前言 本教程将介绍使用OpenCV中的多种线性滤波器来对图像进行平滑处理&#xff0c;主要包…

程序员面试IT技术岗的三大技巧

文章目录 技巧一&#xff1a;深入研究意向企业技巧二&#xff1a;准备常见的面试问题技巧三&#xff1a;总结经历的面试题 在找工作时&#xff0c;面试是每位程序员必须经历的一关。面对众多求职者竞争激烈的情况&#xff0c;我们需要结合自己的现状&#xff0c;针对意向企业做…

复习第三章反射+IO流

一、反射 JAVA反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意一个方法和属性&#xff1b;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机…

在使用Python爬虫时遇到503 Service Unavailable错误解决办法汇总

在进行Python爬虫的过程中&#xff0c;有时会遇到503 Service Unavailable错误&#xff0c;这意味着所请求的服务不可用&#xff0c;无法获取所需的数据。为了解决这个常见的问题&#xff0c;本文将提供一些解决办法&#xff0c;希望能提供实战价值&#xff0c;让爬虫任务顺利完…

机器人科普--AGILOX 叉车

机器人科普--AGILOX 叉车 1 概述2 导航3 驱动轮组4 叉举参考 1 概述 AGILOX 叉车&#xff0c;不需要画地图路径&#xff0c;很厉害。 2 导航 中间路径自由导航&#xff0c;末端规划出轨迹路线&#xff0c;并使用优良的控制器做轨迹追踪。 AGILOX &#xff5c; 10 Min setu…

mysql安装教程保姆级

MySQL免安装本地运行 1.下载MySQL2.创建install.bat3.init.sql 初始创建4.环境变量配置5.运行 install.bat 管理员权限运行6.连接成功遇到的问题 1.下载MySQL ①地址&#xff1a;https://downloads.mysql.com/archives/community/ ②解压 2.创建install.bat 放在mysql>b…

时序预测 | MATLAB实现GRNN广义回归神经网络时间序列预测(多指标,多图)

时序预测 | MATLAB实现GRNN广义回归神经网络时间序列预测(多指标,多图) 目录 时序预测 | MATLAB实现GRNN广义回归神经网络时间序列预测(多指标,多图)效果一览基本介绍程序设计参考资料效果一览 基本介绍 1.MATLAB实现GRNN广义回归神经网络时间序列预测(完整源码和数据) …

hive-date

current_date -- 获取当前系统时间 &#xff0c;获取到的是10 的系统时间 例如 &#xff1a; 2023-07-28 select current_date form edw.test;current_timestamp -- 获取当前的时间戳日期格式&#xff0c;例如 1957-07-27 13:31:21.641 会取到毫秒的一个时间戳格式 select *…

openvino批量推理资料汇总

1、开发者实战 | 基于 C# 和 OpenVINO™ 2023.0部署 YOLOv8 全系列模型_OpenVINO 中文社区的博客-CSDN博客 2、在英特尔 CPU 上加速 Stable Diffusion 推理_OpenVINO 中文社区的博客-CSDN博客 3、 5周年更新 | OpenVINO™ 2023.0&#xff0c;让AI部署和加速更容易_OpenVINO 中…

【C++】—— 多态的基本介绍

前言&#xff1a; 在之前的学习过程中&#xff0c;我们已经对继承进行了详细的学习和了解。今天&#xff0c;我将带领大家学习的是关于 多态 的基本知识。 目录 &#xff08;一&#xff09;多态的概念 1、概念 &#xff08;二&#xff09;多态的定义及实现 1、多态的构成条…

【力扣每日一题】2023.8.1 英雄的力量

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一个数组&#xff0c;让我们找出数组的每个非空子数组&#xff08;不用连续&#xff09;&#xff0c;然后按照公式算出子数组的…

CNN、数据预处理、模型保存

目录 CNN代码读取数据搭建CNN训练网络模型 数据增强迁移学习图像识别策略数据读取定义数据预处理操作冻结resnet18的函数把模型输出层改成自己的设置哪些层需要训练设置优化器和损失函数训练开始训练再训练所有层关机了&#xff0c;再开机&#xff0c;加载训练好的模型 CNN 代码…

计算机网络(2) --- 网络套接字UDP

计算机网络&#xff08;1&#xff09; --- 网络介绍_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131967378?spm1001.2014.3001.5501 目录 1.端口号 2.TCP与UDP协议 1.TCP协议介绍 1.TCP协议 2.UDP协议 3.理解 2.网络字节序 发送逻辑…

Node.js之express框架学习心得

Node.js:颠覆传统的服务器端开发 Node.js是基于Chrome V8引擎构建的JavaScript运行时,它采用了完全不同的开发模型。Node.js使用事件驱动和非阻塞I/O的方式处理请求,通过单线程和异步机制,实现高效的并发处理。这意味着在Node.js中,一个线程可以处理数千个并发连接,大大提…

Debian 12.1 “书虫 “发布,包含 89 个错误修复和 26 个安全更新

导读Debian 项目今天宣布&#xff0c;作为最新 Debian GNU/Linux 12 “书虫 “操作系统系列的首个 ISO 更新&#xff0c;Debian 12.1 正式发布并全面上市。 Debian 12.1 是在 Debian GNU/Linux 12 “书虫 “发布六周后推出的&#xff0c;目的是为那些希望在新硬件上部署操作系统…

从内核源码看 slab 内存池的创建初始化流程

slab cache 机制确实比较复杂&#xff0c;涉及到的场景又很多&#xff0c;大家读到这里&#xff0c;我想肯定会好奇或者怀疑笔者在上篇文章中所论述的那些原理的正确性&#xff0c;毕竟 talk is cheap &#xff0c;所以为了让大家看着安心&#xff0c;理解起来放心&#xff0c;…

让SpringBoot不需要Controller、Service、DAO、Mapper,卧槽!这款工具绝了!

Dataway介绍 Dataway 是基于 DataQL 服务聚合能力&#xff0c;为应用提供的一个接口配置工具。使得使用者无需开发任何代码就配置一个满足需求的接口。整个接口配置、测试、冒烟、发布。一站式都通过 Dataway 提供的 UI 界面完成。UI 会以 Jar 包方式提供并集成到应用中并和应…