当前位置: 首页 > news >正文

【Java基础】day16

day16

一、switch-case 和 if-else 谁更快?

switch-case

在 switch-case 中,case 的值是连续的话,会生成一个 TableSwitch 来进行优化,这样的情况下,只需要在表中进行判断即可。

这里使用 0-4 的连续值来进行测试

如果说多加几个 Case 的值,但是范围控制在比较小的范围时:

这里使用 0-9 之间的不连续的值来进行测试

可以发现仍然使用了一个 TableSwitch 来进行优化。继续加大范围,但是只有少数能使用到,可以看到:

这一部分使用了 LookupSwitch 来进行判断,可以很明显看出就是执行搜索指令来得到目标执行的代码块。那么在这种情况下,switch-case 的效率是比较低的。JVM 虚拟机规范中提到:

Compilation of switch statements uses the tableswitch and lookupswitch instructions. The tableswitch instruction is used when the cases of the switch can be efficiently represented as indices into a table of target offsets. The default target of the switch is used if the value of the expression of the switch falls outside the range of valid indices.Where the cases of the switch are sparse, the table representation of the tableswitch instruction becomes inefficient in terms of space. The lookupswitch instruction may be used instead.

The Java virtual machine specifies that the table of the lookupswitch instruction must be sorted by key so that implementations may use searches more efficient than a linear scan. Even so, the lookupswitch instruction must search its keys for a match rather than simply perform a bounds check and index into a table like tableswitch. Thus, a tableswitch instruction is probably more efficient than a lookupswitch where space considerations permit a choice.

这一部分的大意就是:

switch 语句的编译使用 tableswitch 和 lookupswitch 指令。当 switch 的情况可以有效地表示为目标偏移表中的索引时,使用 tableswitch 指令。如果 switch 表达式的值超出有效索引范围,则使用 switch 的默认目标。在 switch 的情况稀疏的情况下,tableswitch 的 table 表示在空间方面变得低效,也可以使用 lookupswitch 指令。

Java 虚拟机指定 lookupswitch 指令的表必须按键排序,以便实现可以使用比线性扫描更高效的搜索。即使如此,lookupswitch 指令也必须搜索其键以查找匹配项,而不是简单地执行边界检查并索引到类似 tableswitch 的表中。因此,在空间考虑允许选择的情况下,tableswitch 指令可能比 lookupswitch 更有效。

这个 tableswitch 相比 lookupswitch 的转换,什么时候才算是稀疏,这就取决于编译器的决策问题了。这一决策涉及到数理统计,找到效率的执行上的差异来决定。

如果使用的是其他的数据类型进行 switch 的判断时,可以看到这一优化仍然存在。

那么如果 case 之间使用的是不同数据类型呢?

使用 char 类型和 int 类型同时进行判断测试

结果仍然没有改变,最终的底层还是会使用的 ASCII 码进行相关计算。同理来说,如果是 String 类型,则同样换算数字来进行计算,只不过使用的是 String 的 hashcode 方法来获取 String 的对应数值。

if-else
仍然使用之前的测试方法,这里使用连续的小范围的值进行测试:

这里可以看到使用的是逐个进行判断的方式。再使用不连续的方式进行判断:

这里结论没有改变,仍然是使用的逐个判断。

结论

  • 只有在 case 中的条件是连续的或范围相隔不大时,编译器会使用表结构做优化,性能优于 if-else。
  • 其他情况下,switch-case 是逐个分支判断,性能与 if-else 无异。
  • switch-case 中的 case 只能是常量,而 if-else 用途更广一些。

在选择分支较多且连续或者范围相隔不大时,选用 switch-case 结构会提高程序的效率,但 switch 不足的地方在于只能处理字符或者数字类型的变量。if-else 结构更加灵活一些,if-else 结构可以用于判断表达式是否成立,应用范围更广,switch-case 结构在某些情况下可以替代 if-else 结构。

Website
https://godbolt.org/

二、枚举为什么是实现单例模式的最好方式?

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式。它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。由于 JDK 1.5 之后才加入 enum 特性,这种方式在实际开发中用的比较少。

不能通过反射攻击 reflection attack 来调用私有构造方法,所以能绝对防止多次实例化。
枚举类型会在类加载阶段就进行初始化,类加载阶段是数据安全的,所以是线程安全的。

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。

public enum Singleton {  INSTANCE;public void whateverMethod() {  }  
}

三、为什么已经有了 synchronized 还需要 volatile?

从两个角度考虑:

  • 因为 synchronized 是一种锁机制,存在阻塞问题和性能问题;而 volatile 并不是锁,不存在阻塞和性能问题。
  • 因为 volatile 借助了内存屏障来帮助其解决可见性和有序性问题,在有些场景中是可以避免发生指令重排现象。

针对单线程,因为 as-if-serial 语义以及线程执行的时候需要遵守线程内语义 intra-thread semantics(保证重排序不会改变单线程内的程序执行结果),所以 synchronized 修饰的部分不会出现指令重排。那么 volatile 避免的指令重排是不是都可以通过 synchronized 解决呢?
并不是,参考 DCL 双重检查锁的实现。不使用 volatile 可能让其他线程访问到还未初始化完毕的对象,导致获取的对象为空,引发 NPE。

Java 对象在进行创建的时候,通常会有三个步骤:

  1. 分配对象内存
  2. 调用构造器方法,执行初始化
  3. 将对象引用赋值给变量

如果在不使用 volatile 关键字的时候,可能会发生指令重排,导致对象的初始化在多线程环境下的执行顺序发生变化。

指令重排:是机器指令级别的重排,并不是代码行级别的重排。

问题:

① 既然是加锁状态,那么同时刻只会有一个线程执行加锁代码块,且在执行同步块的时候,会刷新主内存,是否也能保证可见性?
是保证了可见性,但是因为指令重排序的问题,会有获取到空对象的风险。

② 在执行初始化的时候,是在同步代码块中的。初始化指令集执行时,是会保证原子性的,也就是只有一个线程能执行这一块代码。这个时候如果未初始化完毕,那么 singleton 对象仍为空。

此刻其余线程如果执行 DCL,会突破第一道检查,在代码块处等待锁的释放。在第一个执行完初始化的线程释放锁之后,其余线程执行代码块,会进行主内存的刷新,所以判断第二次,会得到完整的 singleton 对象。

这个过程中,如果不使用 volatile 修饰 singleton 对象,那么在初始化过程中,会因为指令重排序导致初始化过程出现问题。

指令重排可能导致对象在创建过程中先指向了引用,再执行初始化操作。这样违背了原本的初始化顺序,单线程环境下确实最终的结果不会发生改变。但是多线程下,其余线程在执行第一重检查的时候,可能获取到了只指向了引用还未初始化的空对象,这样就会存在 NPE 风险。

Reference
https://blog.csdn.net/bochuangli/article/details/122862651

四、Java 中的自旋锁怎么实现的?

自旋锁并不是真正的锁,而是让等待的线程先原地等待一下,通常实现方式很简单:

int SPIN_LOCK_NUM = 64;
int i = 0;
boolean wait = true;do {// 尝试获取资源锁wait = xxx
} while (wait && (++i) < SPIN_LOCK_NUM);

在 Java 的 Unsafe 中的 CAS,就使用到了自旋锁。

public final class Unsafe {public final int getAndSetInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var4));return var5;}
}

根据自旋的一些概念,可以得知自旋锁是不能递归的,否则会自己获取自己已经拥有的锁,导致死锁的出现。

一个线程获取了一个自旋锁,在执行时被中断处理程序打断,因此该线程只是暂停执行,并未退出,仍持有自旋锁;而中断处理程序尝试获取自旋锁而获取不到,只能自旋;这就造成一个事实:中断处理子程序 ISR 拿不到自旋锁,导致自旋而无法退出,该线程被中断无法恢复执行至退出释放自旋锁,此时就造成了死锁,导致系统崩溃。

存在这样的问题时候,针对自旋锁,就需要有几个特性:

  • 在临界区持有自旋锁的线程不能休眠,休眠会引起进程切换,CPU 就会被另一个进程占用等无法使用;
  • 持有自旋锁的线程不允许被中断,哪怕是 ISR 也不行,否则就存在 ISR 自旋;
  • 持有自旋锁的线程,其内核不能被抢占,否则等同于 CPU 被抢占。

所以为了避免自旋发生死锁,我们需要针对自旋锁,添加限制条件:

  • 持有自旋锁的线程,不能因为任何原因而放弃 CPU;
  • 基于上述问题,自旋需要添加一个上限时间以防死锁。

Reference
https://juejin.cn/post/6930427187237486605

五、是否所有的 GC 都会触发 STW ?

所有的 GC 都会 STW ,只是存在时间长短区别。原因是可达性分析算法中枚举根节点 GC Roots 会导致所有 Java 执行线程停顿。STW 事件和采用哪款 GC 无关,所有的 GC 都有这个事件。只是现在的 GC 算法越来越精进,减少了 STW 的时间。

并不是只有 Full GC 才会触发 STW,还包括 SafePoint 安全点检查状态。Safepoint 可以理解成是在代码执行过程中的一些特殊位置,当线程执行到这些位置的时候,线程可以暂停。在 SafePoint 保存了其他位置没有的一些当前线程的运行信息,供其他线程读取。可以理解为线程只有运行到了 SafePoint 的位置,它一切的状态信息才是确定的,也只有这个时候,才知道这个线程用了哪些内存。

Reference
https://zhuanlan.zhihu.com/p/437620682
https://www.zhihu.com/question/371699670
https://blog.csdn.net/m0_67401417/article/details/124277023

相关文章:

【Java基础】day16

day16 一、switch-case 和 if-else 谁更快&#xff1f; switch-case 在 switch-case 中&#xff0c;case 的值是连续的话&#xff0c;会生成一个 TableSwitch 来进行优化&#xff0c;这样的情况下&#xff0c;只需要在表中进行判断即可。 这里使用 0-4 的连续值来进行测试 如…...

Neo4j | 一文入门Neo4j!

下面是一些基本的Cypher查询语句&#xff1a; 创建节点 CREATE (n:Person {name:Alice})这会创建一个标签为Person、属性name值为Alice的节点。 创建节点之间的关系 MATCH (a:Person {name:Alice}), (b:Person {name:Bob}) CREATE (a)-[:FRIEND]->(b)这会创建Alice和Bob…...

Python科研数据可视化

在过去的20 年中&#xff0c;随着社会产生数据的大量增加&#xff0c;对数据的理解、解释与决策的需求也随之增加。而固定不变是人类本身&#xff0c;所以我们的大脑必须学会理解这些日益增加的数据信息。所谓“一图胜千言”&#xff0c;对于数量、规模与复杂性不断增加的数据&…...

叫板IT部门和专业软件公司,低代码成为企业数字化的新选择

从2017年政府将“数字经济”写入工作报告&#xff0c;到今年两会将企业数字化转型列为重点议题&#xff0c;数字化的口号已喊了6年。政策对于数字化的支持越来越坚定&#xff0c;令人欣喜的是&#xff0c;越来越多具有远见卓识的企业已将数字化建设作为工作重心。 然而&#xf…...

leetcode 541. 反转字符串 II

题目描述解题思路执行结果 leetcode 541. 反转字符串 II 题目描述 反转字符串 II 给定一个字符串 s 和一个整数 k&#xff0c;从字符串开头算起&#xff0c;每计数至 2k 个字符&#xff0c;就反转这 2k 字符中的前 k 个字符。 如果剩余字符少于 k 个&#xff0c;则将剩余字符全…...

java数据类型的转换以及精度丢失

java数据类型的转换以及精度丢失_long转double会丢失精度吗_ღLiJia的博客-CSDN博客 一.浮点类型在计算机当中的存储 float存储需求是4字节&#xff08;32位&#xff09;, 其中1位最高位是符号位&#xff0c;中间8位表示阶位&#xff0c;后32位表示值 float的范围: -2^128 ~ …...

网络通信基础 - 多路复用技术(频分多路复用、时分多路复用、波分多路复用)

文章目录 1 概述1.1 复用器 MUX 2 分类2.1 频分多路复用 FDM2.2 时分多路复用 TDM2.3 波分多路复用 WDM 1 概述 1.1 复用器 MUX 多路复用技术&#xff1a;把多个低速信道组合成一个高速信道的技术这种技术要用到两个设备&#xff0c;统称为 多路器&#xff08;MUX&#xff09…...

Baumer工业相机堡盟工业相机如何通过BGAPISDK的函数转换示Bayer格式为BGR8格式彩色图像(C++)

Baumer工业相机堡盟工业相机如何通过BGAPISDK的函数转换示Bayer格式为BGR8格式彩色图像&#xff08;C&#xff09; Baumer工业相机Baumer工业相机的Bayer彩色图像的技术背景Baumer工业相机通过BGAPI SDK在回调函数里显示Bayer彩色图像Baumer工业相机在BufferEvent转换Bayer格式…...

一块钱看Android Debug: avc denied 已存在的目录不能访问

某三方应用&#xff0c;使用了USB摄像头&#xff0c;启动应用后功能不能使用&#xff0c;看log有如下错误&#xff0c; denied后面{}里的是要执行的动作,比如append,open,execmod,link等等 scontext指的是域,对应的是te文件 上面报错这条对应te文件是untrusted_app.te, scontex…...

URL 转为QR code(二维码)

推荐一个良心的网站&#xff0c;能够免费地将url、text编码为二维码&#xff0c;而且还能设计logo、颜色等。 https://www.the-qrcode-generator.com/ 如下图&#xff1a; 可以自己定义logo、颜色&#xff1a; 还能查看扫描历史等统计信息&#xff1a; 上述所有功能都是免…...

CentOS软件那么老为什么大家还要用它?

作为一个专业的服务器系统&#xff0c;RHEL 系统理论上每一个软件包都有 RedHat 内部的人员负责维护&#xff0c;这个维护包括长期&#xff08;和系统生命周期一样长&#xff09;的开发、更新、测试、运维等。也就是说你能从 RHEL 系统源上获得的每一个软件包&#xff0c;出现问…...

聚观早报|飞猪:五一出游需求爆发;​特斯拉一季度盈利同比跌20%

今日要闻&#xff1a;飞猪&#xff1a;五一出游需求爆发&#xff1b;特斯拉一季度盈利同比暴跌20%&#xff1b;郑渊洁永远不再发表作品&#xff1b;KargoBot推出无人化自动驾驶卡车&#xff1b;中国6G通信技术研发取得重要突破 飞猪&#xff1a;五一出游需求爆发 4 月 19 日&a…...

Redis缓存雪崩、穿透、击穿

Redis缓存雪崩、穿透、击穿 解决方案正常的缓存流程Redis缓存雪崩Redis缓存雪崩解决方案 Redis缓存穿透Redis缓存穿透解决方案 Redis缓存击穿Redis缓存击穿解决方案 解决方案 布隆过滤器&#xff0c;分布式锁 正常的缓存流程 Redis缓存雪崩 Redis中的key大面积失效&#xff0…...

不要老盯着ChatGPT,这几家公司的产品同样不容小觑

Adept.ai 2022 年成立&#xff0c;致力于一款能够代替人类使用电脑、操控软件的AI助手&#xff0c;旗下模型 ACT-1 还处于雏形阶段&#xff0c;但根据官方示例&#xff0c;已经可以完成通过自然语言指令完成特定的任务。公司创始人曾任OpenAI的工程副总裁&#xff0c;后来进入谷…...

DataBinding 大坑总结(网上我暂时搜不到解决方法)

在使用多Module中使用DataBinding会引发一些奇怪的问题&#xff0c;最近好好的腾出时间来折腾这些奇怪的问题&#xff1a; 1&#xff1a;如果当Module启动DataBinding重启AS启动报错的话&#xff0c;就启用允许多行代码 android { defaultConfig {multiDexEnabled true} } de…...

Linux I/O复用函数的使用情况和select接口的介绍

I/O 复用使得程序能同时监听多个文件描述符&#xff0c;这对于提高程序的性能至关重要。通常&#xff0c; 网络程序在下列情况下需要使用 I/O 复用技术&#xff1a; 1.TCP服务器同时要处理监听套接字和连接套接字 2.服务器同时要处理TCP请求和UDP请求。 3.程序同时要处理多个套…...

leetcode:数字转换为十六进制数(详解)

前言&#xff1a;内容包括&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; 给定一个整数&#xff0c;编写一个算法将这个数转换为十六进制数。对于负整数&#xff0c;我们通常使用 补码运算 方法。 注意: 十六进制中所有字…...

Android 10 设置人脸解锁时,锁屏显示人脸解锁图标

Android 10设置人脸解锁时&#xff0c;锁屏解锁图标不会显示人脸图标&#xff0c;若想显示人脸图标&#xff0c;可参考以下两点进行修改&#xff1a; 1.此处引用人脸图标资源&#xff0c;请参考如下修改&#xff1a; diff --git a/packages/SystemUI/src/com/android/systemui/…...

【嵌入式环境下linux内核及驱动学习笔记-(5-驱动的并发控制机制)】

目录 1、上下文和并发1.1 上下文1.2 共享与临界内核中并发控制机制分为以下几类&#xff1a;并发控制机制的使用场景&#xff1a; 2、 并发控制机制--中断屏蔽3、并发控制机制--原子变量3.1 相关函数原子量类型a.设置原子量的值b.获取原子量的值c.原子变量加减d.原子变量自增自…...

必学宝典 黑马《最新JavaWeb开发教程》上线

对于程序员&#xff0c;所在的行业更迭实属过快&#xff0c;如果是为了找一份好工作&#xff0c;学技术前一定要先了解技术在市场中的需求情况。不然等你学完之后&#xff0c;才发现自己学了已被淘汰、过时的技术&#xff0c;白白浪费了宝贵的学习时间&#xff0c;后悔都来不及…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...