关于java:Guava-临界方案-一致性问题

问题场景促销零碎中,针对某个商品已配置了多个促销流动,且这些流动曾经处于进行中此时新建了一个促销流动,状态为未开始算价查问促销流动时,仅获取到了将来开始的流动,没有获取到已开始的流动,导致商品价格没有优惠问题背景促销零碎为了晋升算价性能,以商品sku为key,促销流动汇合为value,寄存于redis与guava中guava的过期工夫为48小时,若该sku在48小时内没有产生查问,则该本地缓存将过期查问策略为优先查问guava,guava中存在数据则间接返回,不存在则查问redis,再将数据存入guava问题剖析当查问该sku的促销流动时,零碎获取处于进行中的流动,并存放在guava中48小时后,因为没有算价申请,该sku对应的本地缓存过期此时对该sku新建促销流动,B端触发保留策略,将新建的流动保留至redis与guava中,但此处保留至guava实际上是増量保留,并不是通过sku进行全量流动获取,导致guava中仅存在新增的流动此时算价查问该sku的促销流动,仅获取了未失效的流动,导致价格没有优惠问题解决调整guava的写入策略,在新建促销流动时,拉取该sku对应的全量流动,并放入guava中 问题思考本地缓存波及到临界问题时,须要思考缓存写入与读取的计划是否正当,或者说须要思考缓存一致性问题,如果肯定要应用guava,则在DB数据写入后全量从新获取受影响的数据,而不能采纳増量的模式 例如,数据写入DB前,删除redis中的数据,写入DB后加锁从DB获取全量数据存入redis(防止高并发打崩DB),而后告诉所有实例删除guava缓存,则在查问时,所有申请会从redis从新读取数据,当然具体数据删除写入逻辑还需联合具体场景探讨,此处阐明的重点在于受影响的数据须要全量更新在上述案例中,算价查问业务理论应该了解为强统一场景,而guava难以解决一致性问题,在后续优化中应该思考去掉本地缓存,对立从redis中获取数据,且采纳读写锁概念,写数据时不能读取,否则在极其场景下,对于用户感知同一时刻的算价后果可能不同

March 7, 2023 · 1 min · jiezi

关于java:为什么99的程序员都做不好SQL优化

连贯层最上层是一些客户端和链接服务,蕴含本地sock 通信和大多数基于客户端/服务端工具实现的相似于 TCP/IP的通信。次要实现一些相似于连贯解决、受权认证、及相干的平安计划。在该层上引入了线程 池的概念,为通过认证平安接入的客户端提供线程。同样在该层上能够实现基于SSL的平安链接。服务 器也会为平安接入的每个客户端验证它所具备的操作权限。 服务层第二层架构次要实现大多数的外围服务性能,如SQL接口,并实现缓存的查问,SQL的剖析和优化,部 分内置函数的执行。所有跨存储引擎的性能也在这一层实现,如 过程、函数等。在该层,服务器会解 析查问并创立相应的外部解析树,并对其实现相应的优化如确定表的查问的程序,是否利用索引等, 最初生成相应的执行操作。如果是select语句,服务器还会查问外部的缓存,如果缓存空间足够大, 这样在解决大量读操作的环境中可能很好的晋升零碎的性能。 引擎层存储引擎层, 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通 信。不同的存储引擎具备不同的性能,这样咱们能够依据本人的须要,来选取适合的存储引擎。数据库 中的索引是在存储引擎层实现的。 存储层数据存储层, 次要是将数据(如: redolog、undolog、数据、索引、二进制日志、谬误日志、查问 日志、慢查问日志等)存储在文件系统之上,并实现与存储引擎的交互。 和其余数据库相比,MySQL有点不同凡响,它的架构能够在多种不同场景中利用并施展良好作用。次要 体现在存储引擎上,插件式的存储引擎架构,将查询处理和其余的零碎工作以及数据的存储提取拆散。 这种架构能够依据业务的需要和理论须要抉择适合的存储引擎。 存储引擎介绍 大家可能没有据说过存储引擎,然而肯定听过引擎这个词,引擎就是发动机,是一个机器的外围组件。 比方,对于舰载机、直升机、火箭来说,他们都有各自的引擎,是他们最为外围的组件。而咱们在抉择 引擎的时候,须要在适合的场景,抉择适合的存储引擎,就像在直升机上,咱们不能抉择舰载机的引擎 一样。 而对于存储引擎,也是一样,他是mysql数据库的外围,咱们也须要在适合的场景抉择适合的存储引 擎。接下来就来介绍一下存储引擎。 存储引擎就是存储数据、建设索引、更新/查问数据等技术的实现形式 。存储引擎是基于表的,而不是 基于库的,所以存储引擎也可被称为表类型。咱们能够在创立表的时候,来指定抉择的存储引擎,如果 没有指定将主动抉择默认的存储引擎。 建表时指定存储引擎CREATE TABLE 表名( 字段1 字段1类型 [ COMMENT 字段1正文 ] , ...... 字段n 字段n类型 [COMMENT 字段n正文 ]) ENGINE = INNODB [ COMMENT 表正文 ] ;查问以后数据库反对的存储引擎SHOW ENGINES; 创立表 my_myisam , 并指定MyISAM存储引擎CREATE TABLE my_myisam( `id` INT, `name` VARCHAR(10) )ENGINE = MYISAM;创立表 my_memory , 指定Memory存储引擎CREATE TABLE my_memory( `id` INT, `name` VARCHAR(10) )ENGINE = MEMORY;存储引擎特点下面咱们介绍了什么是存储引擎,以及如何在建表时如何指定存储引擎,接下来咱们就来介绍下来下面 重点提到的三种存储引擎 InnoDB、MyISAM、Memory的特点。 ...

March 7, 2023 · 3 min · jiezi

关于java:Jaspersoft使用教程

介绍Jaspersoft:这是基于Eclipse软件开发的图形化报表设计工具。 疾速上手创立我的项目与模板创立我的项目 抉择我的项目类型 输出项目名称 创立模板 抉择模型 模板名称 我的项目目录构造展现 OutLine元素列表 根本元素 <font color="red">模板参数(罕用)</font>Report Name : 模板名称,留神,如果你复制了一份模板文件,这个中央是没有批改的。 Description : 模板形容,这个模板文件是干什么的,起正文作用。 Language : 有三种 Java | groovy | javascript, 这里指定报表表达式应用的语言。 Imports : 引入其余包,自定义,或者第三方 Format Factory Class : 翻译 (指定实现要与此报表一起应用的接口的类的名称。如果省略,将创立的实例) When No Data Type: (当打印的报表数据源中没有数据的状况下,也就是数据源为空的状况下) null: 默认,不抉择。 No Pages: 不打印数据。 Blank Pages:返回一个空白的页面。 All Sections No Detail: 打印除了Detail 之外的所有页面。No Data Section: 把No Data的Band 的也打印进去。 Report 属性 形容 Title On A New Page 示意 Tilte Band 独自一页打印。 Summary On A New Page 示意 Summary 独自一页打印。 Summary With Page Header And Footer 示意在Sumnmary最初一页,也显示Header头 和 Footer脚 Float Column Footer 在最初一页,Column Foot(列脚)是否紧挨着最初一个DetailsIgnore Pagination 疏忽分页 Create bookmarks 创立书签 Dataset 参数 When Resource Missing Type:(当资源的属性谬误时) Null: 默认,为Null。 Empty: 为空。 Key: 输入key Error:报错,异样。 Scriptlet Class: (网上百度)自定义scriptlet,可在报表生成时自定义一些行为。 Resource Bundle: 资源绑定,报表所用资源文件。 Default Data Adapter: 默认数据源,在这里,能够抉择数据源配置在哪里 _数据源配置 ...

March 7, 2023 · 1 min · jiezi

关于java:Java中如何解析SQL语句格式化SQL语句生成SQL语句

昨天在群里看到有小伙伴问,Java里如何解析SQL语句而后格式化SQL,是否有现成类库能够应用? 之前TJ没有做过这类需要,所以去钻研了一下,并找到了一个不过的解决方案,明天举荐给大家,如果您正要做相似内容,那就拿来试试,如果临时没需要,就先理解珍藏(技多不压身)。 JSqlParserJSqlParser是一个用Java编写的SQL解析器,能够将SQL语句解析为Java对象,从而使开发人员可能轻松地剖析、批改和重构SQL查问。 比方,这样的一句SQL语句SELECT 1 FROM dual WHERE a = bSELECT 1 FROM dual WHERE a = b JSqlParser能够将其解析为如下对象构造 SQL Text └─Statements: net.sf.jsqlparser.statement.select.Select └─selectBody: net.sf.jsqlparser.statement.select.PlainSelect ├─selectItems -> Collection<SelectExpressionItem> │ └─selectItems: net.sf.jsqlparser.statement.select.SelectExpressionItem │ └─LongValue: 1 ├─Table: dual └─where: net.sf.jsqlparser.expression.operators.relational.EqualsTo ├─Column: a └─Column: b而后咱们就能够通过其提供的API来拜访这句SQL语句中的各个因素: Statement statement = CCJSqlParserUtil.parse(sqlStr);if (statement instanceof Select) { Select select = (Select) statement; PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); SelectExpressionItem selectExpressionItem = (SelectExpressionItem) plainSelect.getSelectItems().get(0); Table table = (Table) plainSelect.getFromItem(); EqualsTo equalsTo = (EqualsTo) plainSelect.getWhere(); Column a = (Column) equalsTo.getLeftExpression(); Column b = (Column) equalsTo.getRightExpression();}目前,JSqlParser反对了大部分次要的关系型数据库,包含: ...

March 7, 2023 · 1 min · jiezi

关于java:Ovirt中的云主机监控

Ovirt中的云主机监控PollVmStatsRefresher.java Vm刷新调度器 poll() 调度执行办法 @OnTimerMethodAnnotation("poll")public void poll() { if (isMonitoringNeeded(vdsManager.getStatus())) { //负责获取虚拟机列表,并将其存储在VdsManager上进行剖析 VmsListFetcher fetcher = new VmsStatisticsFetcher(vdsManager); long fetchTime = System.nanoTime(); //fetcher.fetch() 调用vdsm接口,获取AllVmStats信息 if (fetcher.fetch()) { //剖析监控到的主机的数据变动并做出解决。相干的更改将被长久化,状态转换和外部命令将相应地产生 getVmsMonitoring().perform(fetcher.getChangedVms(), fetchTime, vdsManager, true); Stream<VdsmVm> vdsmVmsToMonitor = filterVmsToDevicesMonitoring(fetcher.getChangedVms()); processDevices(vdsmVmsToMonitor, fetchTime); } else { log.info("Failed to fetch vms info for host '{}' - skipping VMs monitoring.", vdsManager.getVdsName()); } }}startMonitoring() 启动调度 10-15s public void startMonitoring() { vmsMonitoringJob = schedulerService.scheduleWithFixedDelay( this::poll, VMS_REFRESH_RATE * NUMBER_VMS_REFRESHES_BEFORE_SAVE, VMS_REFRESH_RATE * NUMBER_VMS_REFRESHES_BEFORE_SAVE, TimeUnit.MILLISECONDS);}VmsStatisticsFetcher.java 由PollVmStatsRefresher.poll()调用 ...

March 6, 2023 · 2 min · jiezi

关于java:建议收藏超详细的Canal入门看这篇就够了

概述canal是阿里巴巴旗下的一款开源我的项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&生产,目前次要反对了MySQL(也反对mariaDB)。 背景晚期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需要。不过晚期的数据库同步业务,次要是基于trigger的形式获取增量变更,不过从2010年开始,阿里系公司开始逐渐的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&生产的业务,从此开启了一段新纪元。ps. 目前外部应用的同步,曾经反对mysql5.x和oracle局部版本的日志解析 基于日志增量订阅&生产反对的业务:数据库镜像数据库实时备份多级索引 (卖家和买家各自分库索引)search build业务cache刷新价格变动等重要业务音讯以后的 canal 反对源端 MySQL 版本包含 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x工作原理Mysql的BinLog它记录了所有的DDL和DML(除了数据查问语句)语句,以事件模式记录,还蕴含语句所执行的耗费的工夫。次要用来备份和数据同步。binlog 有三种模式:STATEMENT、ROW、MIXED STATEMENT 记录的是执行的sql语句ROW 记录的是实在的行数据记录MIXED 记录的是1+2,优先依照1的模式记录举例说明举例来说,上面的sqlCOPYupdate user set age=20对应STATEMENT模式只有一条记录,对应ROW模式则有可能有成千上万条记录(取决数据库中的记录数)。 MySQL主备复制原理 Slave 下面的IO线程连贯上 Master,并申请从指定日志文件的指定地位(或者从最开始的日志)之后的日志内容;Master 接管到来自 Slave 的 IO 线程的申请后,通过负责复制的 IO 线程依据申请信息读取指定日志指定地位之后的日志信息,返回给 Slave 端的 IO 线程。返回信息中除了日志所蕴含的信息之外,还包含本次返回的信息在 Master 端的 Binary Log 文件的名称以及在 Binary Log 中的地位;Slave 的 IO 线程接管到信息后,将接管到的日志内容顺次写入到 Slave 端的Relay Log文件(mysql-relay-bin.xxxxxx)的最末端,并将读取到的Master端的bin-log的文件名和地位记录到master- info文件中,以便在下一次读取的时候可能分明的高速Master“我须要从某个bin-log的哪个地位开始往后的日志内容,请发给我”Slave 的 SQL 线程检测到 Relay Log 中新减少了内容后,会马上解析该 Log 文件中的内容成为在 Master 端实在执行时候的那些可执行的 Query 语句,并在本身执行这些 Query。这样,实际上就是在 Master 端和 Slave 端执行了同样的 Query,所以两端的数据是齐全一样的。当然这个过程实质上还是存在肯定的提早的。mysql的binlog文件长这个样子。COPYmysql-bin.003831mysql-bin.003840 mysql-bin.003849 mysql-bin.003858 启用Binlog留神以下几点:Master主库个别会有多台Slave订阅,且Master主库要反对业务零碎实时变更操作,服务器资源会有瓶颈;须要同步的数据表肯定要有主键;canal可能同步数据的原理了解了mysql的主从同步的机制再来看canal就比拟清晰了,canal次要是听过伪装成mysql从server来向主server拉取数据。 ...

March 6, 2023 · 3 min · jiezi

关于java:IntelliJ-IDEA中提高代码开发效率的10个快捷操作

作者:京东批发 张宾 IntelliJ IDEA中进步代码开发效率的10个快捷操作 IntelliJ IDEA提供了一些Java的快捷键,同样也能够帮忙咱们进步日常的开发效率。对于这些快捷操作如下: 1. psvm/main疾速生成 main() 办法在日常开发中,咱们常常须要写main()办法,这时候您也能够应用main或者psvm命令疾速地帮忙咱们创立出main()办法。 2. sout疾速生成println()办法打印输出一些内容到控制台也是频率很高的一个行为,咱们能够应用sout命令疾速创立出System.out.println来打印内容。 3. 通过.var为新对象赋参数这个快捷操作可能很多人就不晓得了,您实能够应用.var快捷地进行赋值操作。 4. 通过.for 疾速创立for循环 5. 疾速条件语句对于条件语句中常见的布尔和字符串操作,这里有一些快捷方式示例: •boolean.if -> if(boolean) •boolean.else -> if(!boolean) •string.null -> if(string==null) •string.nn -> if(string!=null) •string.switch -> switch(string) 6. 通过.try疾速try ... catch 7. 通过 .castvar 疾速类型转换有时必须通过一个一个地输出类名和值来将一个对象转换为另一个对象。直到最近我才晓得能够通过.castvar执行此操作。 8. 通过 .field 疾速扭转属性范畴应用.field能够更轻松地将局部变量晋升到全局范畴。 9. 通过.opt疾速实现Optional 10. 通过.lambda 疾速生成 lambda 语句

March 6, 2023 · 1 min · jiezi

关于java:Spring-Boot-实现装饰器模式真香

前言本文配合实战案例介绍咱们平时 在springboot我的项目外面 怎么去用 装璜器模式、多层装璜怎么玩。 首先先说下装璜器模式是什么 装璜器模式(Decorator Pattern) 也称为包装模式(Wrapper Pattern) 是指在不扭转原有对象的根底之上,将性能附加到对象上,提供了比继承更有弹性的代替计划(扩大原有对象的性能),属于结构型模式。 官网: 装璜器模式的外围是性能扩大,应用装璜器模式能够通明且动静地扩大类的性能。 大白话一点: 有点像是 组合, 就是 我不动原先的业务货色,然而 又想给这个业务货色 加点额定的职责货色。 非入侵的。可拼凑的。实战开搞实例简述预热ISurfTheInternetService 网上冲浪冲浪业务 接口 interface FadInternetCafe implements ISurfTheInternetService 时尚网咖 实现冲浪业务接口,实现重写提供 网上冲浪办法RetroInternetBar implements ISurfTheInternetService 复旧网吧 实现冲浪业务接口,实现重写提供 网上冲浪办法而后在这个原有的冲浪业务下, 不做代码入侵, 咱们想给网上冲浪冲浪业务加一点额定的职责,比方XXX,XXX啥的xxx业务。 于是乎,咱们开始玩装璜器设计模式 SurfDecorator implements ISurfTheInternetService 网上冲浪装璜器怎么玩的?看代码, 前面还会说怎么多层装璜 。 Spring Boot 根底就不介绍了,举荐看这个收费教程: https://github.com/javastacks/spring-boot-best-practice事不宜迟。 ① ISurfTheInternetService.java 网上冲浪冲浪业务 接口/** * @Author: JCccc * @Date: 2022-10-07 15:18 * @Description: 网上冲浪 */public interface ISurfTheInternetService { /** * 冲起来 */ void doSurfing();}② FadInternetCafe.java 时尚网咖业务实现类import com.example.mydemo.service.ISurfTheInternetService;import org.springframework.stereotype.Service;/** * @Author: JCccc * @Date: 2022-10-07 15:21 * @Description: 时尚 网咖 */@Service("fadInternetCafeService")public class FadInternetCafe implements ISurfTheInternetService { @Override public void doSurfing() { System.out.println("在时尚 网咖 ,网上冲浪咯~"); }}③ RetroInternetBar.java 复旧网吧业务实现类import com.example.mydemo.service.ISurfTheInternetService;import org.springframework.stereotype.Service;/** * @Author: JCccc * @Date: 2022-10-07 15:21 * @Description: 复旧 网吧 */@Service("retroInternetBarService")public class RetroInternetBar implements ISurfTheInternetService { @Override public void doSurfing() { System.out.println("在复旧 网吧 ,网上冲浪咯~"); }}先到这, 写个controller办法,模仿平时搬砖实在场景 : ...

March 6, 2023 · 2 min · jiezi

关于java:奶奶看了都会GPT35接入企业微信可连续对话

1.间断对话成果小伙伴们,这周OpenAI放出大招,凋谢了GPT3.5的API。说简略点,就是提供了和GPT页面对话一样模型的接口。而之前接的GPT接口都是3.0,并不是真正的GPT。废话少说,先来看看成果,这次最大的不同是能间断对话了 2.筹备工作这次更新之后,国内服务器已没法间接拜访openai的接口,须要本人买个国外的服务器。 一台海内服务器(服务器上安装Java8,操作系统选Ubuntu,如果用windows,要本人钻研)注册好的企业微信有额度的OpenAI账号,并创立了账号的API key我的我的项目代码,代码下载:GPT3.5接入企业微信且反对间断对话3.企业微信3.1增加机器人注册不多说了,本人搞定。增加自建利用操作如下: PC端登录地址:https://work.weixin.qq.com/wework_admin/frame#apps 先登录创立好的企业微信账号 而后增加自建利用 填写名称、上传logo图片,创立利用。 3.2设置API接管 这一步比较复杂,操作讲细点 3.2.1 获取token、EncodingAESKey、企业ID、利用配置 这一步我随机生成这两个字段的值,只作为文章里演示啊,图上标注了我的项目代码里须要改的配置名称。拿到下面5个信息后,替换代码里的对应的5个字段 chatGPT的账号API key自行创立,不会的能够搜寻找找 3.2.3 公布利用到海内服务器配置批改完后,可通过IDEA右上角的maven性能打包,或是在我的项目目录下运行mvn package命令打包,打包实现后,会在我的项目的target目录下失去一个application.jar文件 通过ssh命令登录你的海内服务器,有不会的可自行百度或是看阿里云下面服务器的登录形式文档近程连贯Linux服务器 接着服务器上运行rz命令(如没有,按零碎提醒装置即可),上传application.jar文件。 最初通过nohup java -jar application.jar >log.txt &运行程序,成果如下 留神零碎的运行端口是8080,服务器要配置防火墙白名单。 而后浏览器拜访http://[你的服务器IP]:8080/receiveMsgFromWechat 呈现Whitelabel Error Page字样的提醒就阐明启动胜利了 3.2.4 启用API接管咱们找到配置API接管的页面,把上一步的让你在浏览器关上的URL填进去,而后点击保留,就实现配置了 3.2.5 设置可信IP最初把本人的IP加到可信IP里就功败垂成了 4. 测试成果发送开始间断对话即可进入间断对话模式,发送完结间断对话退出间断对话模式,间断对话次数限度可自行批改 目前这种形式适宜小规模应用,如果须要大规模多人应用,整体的架构要从新设计哦~不过看到这篇文章并且想本人动手做的人,应该都是小规模用。

March 5, 2023 · 1 min · jiezi

关于java:BlockQueue-基于TransferStack的SynchronousQueue

ThreadPoolExecutor以BlockingQueue存储待执行工作,包含SynchronousQueue、LinkedBlockingQueue和ArrayBlockingQueue,明天的目标是源码角度深入研究SynchronousQueue。 之后打算是持续钻研LinkedBlockingQueue和ArrayBlockingQueue,搬开所有绊脚石之后再开始线程池。 基本概念#BlockingQueueBlockingQueue是SynchronousQueue的爹,他们的先人是Queue,所以他们都会听从Queue的一些根本逻辑:比方按程序存入数据、按程序(FIFO或者LIFO)取出数据,都是从队首(head)获取数据,FIFO队列新数据从队尾入队、LIFO队列队列新数据入队首。 对于BlockingQueue,咱们还是认真看一下他的javaDoc: BlockingQueue是一个非凡的Queue:当获取数据的时候阻塞期待队列非空,存入数据的时候阻塞期待队列可用(有界队列未满)。BlockingQueue的办法(获取数据或者存入数据)不能立刻胜利、然而未来某一时间点可能会胜利的状况下,有四种不同的解决形式:一种是抛出异样,第二种是返回非凡值(空值或者false),第三种是无限期阻塞以后线程晓得胜利,第四种是阻塞期待设定的工夫。具体如下表: Throws exception Special value Blocks Times out Insert add add(e) offer offer(e) put put(e) offer(Object, long, TimeUnit) offer(e, time, unit) Remove #remove remove() #poll poll() #take take() #poll(long, TimeUnit) poll(time, unit) Examine #element element() #peek peek() not applicable not applicable BlockingQueue不承受空值null,尝试写入null会抛出异样,因为BlockingQueue用null示意操作失败。BlockingQueue能够是有界的也能够是无界的,有界队列保护残余容量属性remainingCapacity,超出该属性后会阻塞写入操作。无界队列没有容量限度,始终保护remainingCapacity为Integer.MAX_VALUEBlockingQueue是线程平安的,BlockingQueue的实现类的办法都是原子操作、或者通过其余并发管制形式实现线程安全性。基本概念SynchronousQueueSynchronousQueue是一个每次写入数据都必须期待其余线程获取数据(反之亦然)的BlockingQueue。SynchronousQueue没有容量的概念、一条数据都不能存储。你不能对SynchronousQueue队列执行peek操作因为只有执行remove操作能力获取到数据,只有其余线程要remove数据的时候你能力插入数据,你也不能进行迭代因为他基本就没有数据。队列的队首数据就是第一个写入数据的线程尝试写入的数据,如果没有写入线程则队列中就没有数据可获取,poll()办法会返回null。对于汇合类的其余办法、比方contains,SynchronousQueue的返回等同于空集合。SynchronousQueue不容许空(null)元素。通过结构参数设置fairness为true提供偏心队列,偏心队列听从FIFO(先进先出)。基本概念 TransfererTransferer是SynchronousQueue的外部类,他的JavaDoc也很长: Transferer扩大实现了W. N. Scherer III和M. L.Scott在"Nonblocking Concurrent Objects with Condition Synchronization"中形容的双栈(dual stack)或者双队列(dual queue)算法。后进先出(Lifo)栈用来实现非偏心模式,先进先出(Fifo)队列用来实现偏心模式。两者的性能是一样的,个别状况下Fifo反对大吞吐量、Lifo maintains higher thread locality(道歉,没搞懂什么意思)。dual queue(或dual stack)在任一时间要么持有数据(写入操作提供的)、要么持有申请(获取数据的申请),向正好持有数据的队列申请数据、或者向持有申请的队列写入数据被称之为"fulfill"。最乏味的个性是对队列的任何操作都能晓得队列过后处于什么状态,因而操作不必要上锁。queue和stack都扩大自虚构类Transferer,Transferer定义了一个办法transfer,能够同时实现put和take性能。把put和take对立在一个transfer办法中的起因是dual数据结构使得put和take操作是对称操作,所以两个办法的大部分代码都能够被合并。好了,无关JavaDoc形容的个性就到这里了,SynchronousQueue的JavaDoc很不容易了解,代码也是。 开始剖析源码: 构造函数:决定SynchronousQueue底层数据结构是用Queue还是Stack因为SynchronousQueue是比拟非凡的:不存储数据的(JavaDoc提到过),所以须要明确的是相干汇合办法的返回也相应比拟非凡,比方size=0,isEmpty=true等等,这部分源码就不看了,特地简略队列存、取数据的办法,最终调用的都是Transferer的tranfer办法,所以咱们次要就看这个办法SynchronousQueue构造方法提供一个有参构造方法接管一个布尔量fair,咱们后面说过,Queue是偏心的、Stack是非偏心的,所以fair=true的话创立TransferQueue,否则创立TransferStack。 TransferStack#SNodeTransferStack(TransferQueue也一样)的源码尽管不多,然而必须首先理解分明他的数据结构,否则不太容易读懂。 节点SNode:也就是存储到栈内的内容,留神我这里没有说存储在栈内的数据而是说内容,是因为TransferStack的特殊性导致说数据容易引起误会:栈内有两种类型的节点,一种是“data”,能够了解为“生产者”放到栈内等得消费者生产的数据,另一种是“request”,能够了解为消费者的生产申请,也就是说申请和数据都会入栈,都属于“节点”。 TransferStack通过外部类SNode定义节点,次要属性: static final class SNode { volatile SNode next; // next node in stack volatile SNode match; // the node matched to this volatile Thread waiter; // to control park/unpark Object item; // data; or null for REQUESTs int mode;next:下一节点。match:以后节点的匹配节点,比方一个申请数据的Request节点入栈后,正好有一个data节点入栈,他们两个如果匹配胜利的话,match就是对方节点。waiter:如果以后节点即便在自旋期待后依然没有被匹配,比方一个申请线程发送获取数据的申请后,该申请会以申请节点(Request节点)入栈,始终没有数据送进来,则以后节点的waiters就记录为以后线程,之后以后线程本人挂起,期待匹配。这个期待匹配的过程是被动的,只能被另外一个data线程送进来的data节点匹配,匹配之后data线程通过Request节点的waiters获取到其对应的线程后唤醒该线程。item:data类的节点,记录送进来的待生产的数据,Request类的节点,item为null。mode:以后节点的mode,有三个mode:data mode示意以后节点是数据节点(生产者发来的),Request mode示意以后节点是申请节点(消费者发来的),还有一个比拟非凡的mode是:匹配中的数据节点或匹配中的申请节点,这个mode前面剖析tranfer代码的时候再说。head:头节点,栈构造嘛,入栈节点始终是头节点,也只有头节点具备失常出栈的权限。 ...

March 5, 2023 · 3 min · jiezi

关于java:彻底干掉-BeanUtils最优雅的-Mapstruct-增强工具全新出炉

背景在当初风行的零碎设计中,个别会将对象模型划分为多个档次,例如 VO、DTO、PO、BO 等等。这同时也产生了一个问题,常常须要进行不同层级的模型之间互相转换。 针对这种问题,目前常会采纳三种计划: 调用每个字段的 getter/setter 进行赋值。这个过程,干燥且乏味,容易出错的同时,极易容易造成代码行数迅速收缩,可浏览性差。apache-commons、Spring 等提供的 BeanUtil 工具类,这种工具类应用十分不便,一行代码即可实现映射。但其外部采纳反射的形式来实现映射,性能低下,呈现问题时,调试艰难,当须要个性化转换时,配置麻烦,十分不倡议应用,特地是对于性能要求比拟高的程序中。mapstruct:这个框架是基于 Java 正文处理器,定义一个转换接口,在编译的时候,会依据接口类和办法相干的注解,主动生成转换实现类。生成的转换逻辑,是基于 getter/setter 办法的,所以不会像 BeanUtil 等耗费其性能。下面的三种办法中,最优良的莫属 mapstruct 了,当然,美中不足的就是,当零碎较为简单,对象较多且结构复杂,又或者有的我的项目设计中会定义多层对象模型(如 DDD 畛域设计),须要定义较多的转换接口和转换方法,这也是一些开发者放弃 Mapstruct 的次要起因。 这里,就要给大家介绍一个 Mapstruct 的加强包 —— Mapstruct Plus,一个注解,能够生成两个类之间的转换接口,使 Java 类型转换更加便捷、优雅,彻底摈弃 BeanUtils。 疾速开始上面演示如何应用 MapStruct Plus 来映射两个对象。 假如有两个类 UserDto 和 User,别离示意数据层对象和业务层对象: UserDtopublic class UserDto { private String username; private int age; private boolean young; // getter、setter、toString、equals、hashCode}Userpublic class User { private String username; private int age; private boolean young; // getter、setter、toString、equals、hashCode}增加依赖引入 mapstruct-plus-spring-boot-starter 依赖: ...

March 5, 2023 · 3 min · jiezi

关于java:滚蛋吧正则表达式

大家好,我是良许。 不晓得大家有没有被正则表达式摆布过的恐怖?看着一行火星文一样的表达式,尽管每一个字符都意识,但放在一起间接就让人蒙圈了~ 你是不是也有这样的操作,比方你须要应用「电子邮箱正则表达式」,首先想到的就是间接百度上搜寻一个,而后采纳 CV 大法神奇地接入到你的代码中? 别害羞,很失常啦~(我不会通知你我也是这么干的) 明天给大家举荐两个正则表达式神器,让你 1 秒找到适宜本人的表达式,同时也能让你知其然更知其所以然,能够让你进步编写正则表达式的能力! 神器一:我恨正则表达式正如这个神器的名称所形容的,作者正是痛恨正则表达式,而后就本人开发了一个网站,能够搜寻出大部分常见的正则表达式的写法。 技术人嘛,能本人入手的决不瞎 BB 。 界面非常简洁,只有一个大大的搜寻框。你只有把你想找的正则表达式的关键词放在搜寻框里,就能疾速找到对应的表达式,非常不便。 搜寻框上面还有 9 个高频应用的正则表达式,点击就能中转。 比方点击 username ,这个表达式是用来匹配网站用户名是否标准。 能够看到,不仅给出了表达式,同时上面还有对应的匹配例子。 而且,最重要的是,它还给出了对应的正则表达式图解,让你一眼就能看进去这个正则表达式的匹配过程,比间接看那堆火星文一样的字符不要太直观! 这还不够,他还怕你不懂正则的语法,贴心地把用到的语法贴在上面,让你晓得这个表达式为什么要这么写。几乎是保姆级神器! 有了它,妈妈再也不必放心我不会写正则表达式了! 我的项目:https://github.com/geongeorge/i-hate-regex网站:https://ihateregex.io/expr/phone神器二:正则表达式可视化我认为 ihaveregex 曾经够牛逼了,直到我发现上面这个神器,更加刷新我的三观! 首先,常见的几十个正则表达式该有的都有,点击就能应用,这是基本操作,不解释。 而后,它也有可视化界面,也不啰嗦。 上面就是它牛逼之处了。 第一个牛逼的中央,就是能够本人编写正则表达式,而后输出待匹配内容,进行验证! 毕竟咱们的业务需要多种多样,不可能每个需要都能找到对应的表达式,总该须要本人入手写。 但本人入手写,鬼晓得本人写得对不对。有了这个神器,就能够一步到位,间接验证! 你认为这就完了?图样图森破! 它还能够帮你生成支流语言的实例代码,更加不便你使出 CV 大法,几乎丧心病狂! 除此之外,在左边它也把正则的规定都帮你贴出来了,不便你查阅。 这个神器,真的是把正则表达式该有的货色都给你安顿得明明白白,省时省力提高效率! 链接:http://tool.rbtree.cn/regtool/小结明天介绍的两个神器,都能够找到最罕用的正则表达式,拿来即用,同时也都有可视化图解,让你更加明确表达式匹配过程,更加直观。 正当利用这两个神器,肯定能够进步你的工作效率,同时也能更快把握正则表达式,让你真正成为一位高手! 赶快去用起来吧! 学习编程,千万不要急于求成,肯定要多读一些经典书籍,多看源码,多下苦功夫去死磕代码,这样技术能力出息。给大家分享一些程序员必读经典书籍,肯定要多读几遍: 收费送给大家,只求大家金指给我点个赞! 程序员必读经典书单(高清PDF版) 有播种?心愿老铁们来个三连击,给更多的人看到这篇文章举荐浏览: 干货 | 程序员进阶架构师必备资源免费送刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!欢送关注我的博客:良许Linux教程网,满满都是干货!

March 5, 2023 · 1 min · jiezi

关于java:一个Bug让人类科技倒退几十年

大家好,我是良许。 前几天在直播的时候,问了直播间的小伙伴有没人晓得「千年虫」这种神奇的「生物」的,竟然没有一人可能答得上来的。 所以,明天就跟大家科普一下这个人类历史上最大的 Bug 。 1. 全世界的恐慌一个Bug会让人类的科技倒退几十年? 这不是危言耸听,而是实在存在的历史。 1999 年的跨年夜,人们个体陷入恐慌,大家忙着取现金,忙着屯粮,还有人钻进山洞避难,他们感觉世界末日行将到来。 进入新千年原本是一件十分值得纪念的日子,毕竟这辈子这样的机会也不多,但为何全世界却乱成了一锅粥呢? 引起这场凌乱的配角,就是驰名的「千年虫」。 2. 什么是千年虫当初新一代的程序员大多是 95 后或 00 后,千年虫来袭的时候他们要么还没出世,要么还在玩泥巴,所以对这场凌乱简直没有任何感知。 所谓的千年虫,并不是一种生物,而是一种计算机 Bug 。 那这又是一种怎么的 Bug ,为何又造成如此大的凌乱呢? 简略来讲,千年虫是因为计算机外部工夫的存储不合理,导致工夫错乱,从而计算机罢工。1999 年到 2000 年尽管只隔了一年,但实际上这是世纪交替,导致系统工夫错乱,随后解体。 而这些都是有历史渊源的。 计算机刚诞生的时候,因为技术的限度,导致计算机存储设备十分低廉。而且贵也就算了,这些存储设备的容量偏偏又十分小(也是没方法的事),所以程序员们在写代码的时候,真的是对每个字符都要精打细算。 想想当初的程序员,能够随便分配内存空间,不为存储而发愁,真的是太幸福了。为了节约内存,有位靓女想出了用 6 位数表白工夫的方法,比方 1989 年 10 月 1 日就写成了 891001 。 这位靓女,就是软件之母的格雷斯霍珀。也就是她,发现了人类历史上的第一个 Bug ,同时也制作了人类历史上最大的 Bug 。 当然,这里并不是在贬斥她,在软件行业她给世人做出的奉献都是引人注目的。更何况,哪个程序员没写过 Bug ?霍珀创造的 6 位数工夫记录法因为非常简略并且十分省内存,所以大家都纷纷效仿。也正是这 6 位数工夫记录法的大规模应用,使得千年虫开始轻轻潜伏在人类社会,只等千禧年开始反扑人类。 有计算机常识的小伙伴应该很快发现,这种工夫记录法因为年份的前两位被抹去(如 1989 年间接记为 89 ),这就会造成在进入 21 世纪时,呈现工夫回退的景象。 也就是说,在 2000 年的时候,计算机会认为以后是 1900 年,工夫凌乱就会由此而产生。 在编程世界里,工夫是个十分重要的参数,有很多业务是依赖工夫而发展的。一旦工夫错乱,可能会引起各种各样莫名其妙的故障。 这就是驰名的千年虫问题。 3. 危机潜伏实际上,在上世界五十年代末的时候,有位叫鲍勃贝默的计算机科学家就发现了这个暗藏的大问题,于是他就开始到处奔波,想让大家意识到这件事件的严重性。 ...

March 4, 2023 · 1 min · jiezi

关于java:Attempt-to-invoke-virtual-method-‘javalangString

报错日志 java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.os.Bundle.getString(java.lang.String)' on a null object reference at com.example.test1.MainActivity$1.onActivityResult(MainActivity.java:57)java代码报相似的谬误,肯定呈现相似null的空值去toString或getstring()之类的计算了。仔细检查是否呈现了null空值。

March 4, 2023 · 1 min · jiezi

关于java:不是吧2000块的英语听读应用长这样

根本需要 前端局部: 1、拜访地址为:http://website/zrpd?useid=1234,其中userid是用户标识,页面须要依据不同的标识值返回不同的信息。 (1)如果该userid字段不存在,或者该userid在后端不存在,则整个页面提醒:您输出的拜访地址有效,请从新输出。 (2)如果userid和访问者的ip不对应,则整个页面提醒:您无权限拜访该地址,请从新输出。 2、页面的UI设计为单页面,无跳转,具体设计如下:整个页面分为两个区域,一个区域为通过单词搜寻(残缺的单词),一个区域为通过音素搜寻(字母或字母组合),互不烦扰,互相独立。未搜寻前,下方显示为空。 3、单词搜寻区搜寻进去的单词,分段赋予色彩显示,比方department,分为三段,de、part、ment,每一段用一种色彩,色彩应用的程序为(红绿橙蓝紫,超过5个字段,则循环这5个色彩):字段1字段2字段3字段4字段5,例句中该单词用红色标出。 4、音素搜寻区搜寻进去的单词,色彩显示同单词搜寻区搜寻进去的单词,除此之外,还须要额定在搜寻的音素下方画横线标出。 5、点击搜寻按钮时,不整体刷新,后盾返回数据后,仅刷新显示区域。 6、点击喇叭图片能够播放声音,播放声音时,喇叭图片有动效。 7、单词图片点击能够放大,放大后右上角有叉号,点击能够敞开放大的图片。 后端局部: 1、以json字符串或xml等作为数据源文件,具体格局能够依照不便编程和提高效率来设计。 2、通过单词搜寻(大略有3000个单词)和通过音素(大略有100个音素)搜寻,设计两个独立的数据源文件,数据源在启动后加载到内存,无需每次搜寻时从新加载,如批改数据源文件,须要重启后盾服务能力失效。 3、userid须要设计一个独立的数据源文件,每次页面拜访均需检索数据源文件,做到批改了数据源文件,即刻失效,无需重启后盾服务。每一个userid对应一个字符串(用于在页面上显示),一个ip地址列表(用于校验非法的拜访ip地址,0.0.0.0代表所有地址均非法)。 编程语言 html+css+js+ajax+springboot+json 环境与工具 开发工具:hbuilderx、idea、json开发环境:jdk 实现思路 1.前端:应用前端三件套,灵便布局和渲染页面,次要技术点在于js的使用,应用ajax申请后端数据,以及管制、监听音频的播放暂停等。 2.后端:应用传统的web开发思路,编写页面申请数渲染,采纳json模式的数据库,单词、音素、以及账号零碎都应用json建设独立的数据结构,响应给前端动静渲染到页面上。 3.数据库:应用的是json文件保留json模式的数据,且json文件能够部署在tomcat或者本地。 成果展现 看完技术大佬们的解题思路,有没有学到什么呢?想要做兼职的小伙伴能够加咱们哦!

March 4, 2023 · 1 min · jiezi

关于java:得物供应链复杂业务实时数仓建设之路

1 背景得物供应链业务是纷繁复杂的,咱们既有 JIT 的现货模式两头夹着这大量的仓库作业环节,又有到仓的寄售,品牌业务,有非常复杂的逆向链路。在这么简单的业务背地,咱们须要精细化关注人货场车的效率和老本,每一单的及时履约状况,要做到这一点咱们须要各粒度和维度的数据来撑持咱们的精细化治理。 1.1 业务晚期业务晚期,业务反馈咱们后盾管理系统某些报表查问慢。查问代码可知,如下图: 这种景象个别体现为: 大表 JOIN,rdbms 不善于做数据聚合,查问响应慢,调优艰难;多表关联,索引优化,子查问优化,加剧了复杂度,大量索引,读库磁盘空间收缩过快;数据量大,多维分析艰难,跨域取数,自助拉到实时数据艰难等。一方面起因是零碎设计之初,咱们次要关注业务流程功能设计,事务型业务流程数据建模,对于将来外围指标的落地,特地是要害实时指标落地在业务快速增长的状况下如何做到十分好的撑持。mysql 在此方面越来越顾此失彼。 另外一方面起因是 mysql 这种 oltp 数据库是无奈满足实时数据分析需要的,咱们须要摸索一套实时数据架构,拉通咱们的履约,仓储,运配等各域的数据,做无效串联,因而咱们开始了咱们的实时数据架构摸索,下图是咱们一些思考。 附:数据视角的架构设计也是零碎架构设计的重要组成部分。 2 架构演变2.1 原始阶段2.1.1 通过 Adb(AnalyticDB for MySQL)实现实时 join通过阿里云 DTS 同步间接将业务库单表实时同步到 Adb,通过 Adb 弱小的 join 能力和齐全兼容 mysql 语法,能够执行任意 sql,对于单表大数据量场景或者单表和一些简略维表的 join 场景体现还是不错的,然而在业务简单,简单的 sql rt 很难满足要求,即便 rt 满足要求,单个 sql 所耗费的内存,cpu 也不尽人意,能撑持的并发量很无限。 2.1.2 通过 Otter 实现大宽表的建设 基于 Canal 开源产品,获取数据库增量日志数据并下发,上游生产增量数据间接生成大宽表,然而宽表还是写入 mysql 数据库,实现单表查问,单表查问速度显著晋升,无 olap 数据库的常见做法,通过宽表缩小 join 带来的性能耗费。 然而存在以下几个问题: 尽管 otter 有不错的封装,通过数据路由能做一些简略的数据拼接,但在调试上线复杂度上仍然有不小的复杂度;otter 假装 mysql 从库同时要去做 etl 逻辑,把 cdc 干的活和实时 ETL 的活同时干了,耦合度较高。2.2 实时架构 1.02.2.1 flink+kafka+ClickHouse在上述调研尝试后都没有解决基本的问题,咱们开始把指标建设规范的实时数仓的思路上来,在 20 年 olap 没有太多的可选项,咱们把指标放在 clickhouse 上。 ...

March 4, 2023 · 2 min · jiezi

关于java:MySql-索引限制

给json中的字段创立索引alter table customer add index usera((CAST(custom_json ->> '$.userId' as CHAR(500))));创立索引数量限度报错Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

March 4, 2023 · 1 min · jiezi

关于java:MongoDB-索引限制

汇合中索引不能超过64个索引名的长度不能超过128个字符一个复合索引最多能够有31个字段

March 4, 2023 · 1 min · jiezi

关于java:Elasticsearch-常用命令

查看索引GET _cat/indices?v&pretty 查看映射GET test/_mappings 依据ID删除文档DELETE test/_doc/RzrGCoYBybaXcbzwDnJf 创立映射PUT test/_mapping{ "properties": { "customerJson": { "type": "object" }, "operationMode":{ "type":"integer" }, "age":{ "type":"long" }, "birthday":{ "type":"date", "format":"yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || yyyy/MM/dd HH:mm:ss|| yyyy/MM/dd" }, "name":{ "type":"text" }, "friend":{ "type":"nested", "properties":{ "modifiedTime":{ "format":"yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || yyyy/MM/dd HH:mm:ss|| yyyy/MM/dd", "type":"date" } } } }}设置字段数量PUT test/_settings{ "index.mapping.total_fields.limit": 3}设置嵌套构造对象数量PUT test/_settings{ "index.mapping.nested_objects.limit": 2000}设置正本数量PUT test/_settings{"index" : { "number_of_replicas" : 0 }}设置映射模式1、true 是默认值,主动增加新呈现的字段到 mapping 中。2、false,不增加新呈现的字段到 mapping 中,但能够在 doc 中保留新字段。3、"strict" 不容许呈现新字段,会报错。其中嵌套构造外部反对独自配置。 ...

March 4, 2023 · 2 min · jiezi

关于java:携程Java三面面经已OC

分享一位读者投稿的携程校招 Java 岗位的面经。 上面是注释。 集体背景:双非本,机械业余转码。 携程在正式面试之前,会有一个性情测试(40分钟)。性情测试之后,大略过一周进行口试。口试之后,会邮件告诉是否通过并预约第一轮面试工夫。 一般 offer 个别只有两面,如果是 sp 或者 ssp 的话,技术风貌似是三面。 携程的面试难度个别,效率比拟高,面试体验还是不错的。 一面(45min)次要是问八股,难度较低。 自我介绍;过程和线程的区别;并行和并发的区别;synchronized 的作用;synchronized 和 ReentrantLock 的区别,如何抉择;ThreadLocal 应用过程中可能存在的问题(内存泄露);ThreadLocal 内存泄露问题是怎么导致的;我的项目中是如何创立线程池的,什么不必Executors 去创立线程池;晓得的本地缓存,抉择 Caffeine 的起因;Redis 这类缓存和 Caffeine 的区别;Redis 中常见的数据结构,利用场景;缓存穿透和缓存雪崩的区别,解决办法;MySQL 和 Redis 怎么保持数据统一;一个 SQL 口试题,join 多表查问(共享屏幕)。答案: Java 并发常见面试题总结(上)、Java 并发常见面试题总结(中)、Java 并发常见面试题总结(下)Java高性能缓存库- Caffeine - 风之筝缓存根底常见面试题总结(付费)Redis常见面试题总结(上)、Redis常见面试题总结(下)SQL常见面试题总结二面(50min)二面次要还是八股。 自我介绍;应用多线程可能存在的问题;线程池原理;聊聊ThreadLocal (概念+一些利用举例+常见的内存透露问题);JVM 内存模型和垃圾回收;用到过内存剖析工具吗;应用索引能带来什么益处,你我的项目中是怎么应用的;索引底层常见的数据结构,MyISAM 引擎和 InnoDB 引擎用的是哪种;聚簇索引和非聚簇索引;最左前缀匹配准则;造成索引生效的常见起因你晓得那些,我的项目中遇到过索引生效问题吗;如果有一条 SQL 语句执行的很慢,如何进行优化;我的项目中是如何应用 ES的;ES 检索比拟快的起因,为什么 MySQL 不行;讲一下倒排索引;手写一个生产者消费者队列;反诘。答案: Java 并发常见面试题总结(上)Java 并发常见面试题总结(下)Java 内存区域详解、JVM 垃圾回收详解Java内存剖析相干工具MySQL索引详解MySQL执行打算剖析Elasticsearch常见面试题总结(付费)HR面集体的根本信息;对携程的理解;三个词形容本人;手里还有哪些 offer;平时的兴趣爱好;抉择工作的理由排序(薪资、加班状况之类的)。英语测评HR 面之后,还会有一个英语测评,题目比拟多,对英语不好的同学不太敌对。题型大略是浏览、演讲、听力这些。 不过,也不必放心,应该不太会因为英语测评的体现刷掉你,但英语测评还是可能会对你的面试评估造成影响,能做好还是要尽量做到最好。

March 3, 2023 · 1 min · jiezi

关于java:全局视角看技术Java多线程演进史

作者:京东科技 文涛 全文较长共6468字,语言通俗易懂,是一篇具备纲要性质的对于多线程的梳理,作者从历史演进的角度讲了多线程相干常识体系,让你知其然知其所以然。前言2022年09月22日,JDK19公布了,此版本最大的亮点就是反对虚构线程,从此轻量级线程家族再添一员大将。虚构线程使JVM解脱了通过操作系统调度线程的解放,由JVM本身调度线程。其实晚期sun在Solaris操作系统的虚拟机中实现过JVM调度线程,基于其复杂性,和可维护性思考,最终都回归到了由操作系统调度线程的模式。 长安归来锦衣客,昨日城南起新宅。回忆这一路走来,对于多线程的概念令人烟花缭乱,网上相干解说也举不胜举,但总感觉短少一个全局性的视角。为此笔者系统性的梳理了Java对于多线程的演进史,心愿对你把握多线程常识有帮忙。 本文不讲什么: 1 不讲某些技术点的具体实现原理,不拆解源码,不画图,如果从本文找到了你感兴趣的概念和技术能够自行搜寻 2 不讲反对并发性的库和框架,如Quasar、Akka、Guava等本文讲什么 1 讲JDK多线程的演进历史 2 讲演进中某些技术点的性能原理及背景,以及解决了什么问题 3 讲针对某些技术点笔者的认识,欢送有不同认识的人在评论区探讨里程碑老规矩,先上个统计表格。其中梳理了历代JDK中对于线程相干的外围概念。在这里,做一个可能不太失当的比喻,能够将多线程的演进映射到汽车上,多线程的演进别离经验了手动档时代(JDK1.4及以下),自动档时代(JDK5-JDK18),主动驾驶时代(JDK19及当前)。这个比喻只为了通知读者JDK5当前能够有更难受姿态的驾驭多线程,JDK19当前更是冲破了单纯的难受,它给IO密集型服务的性能带来了质的飞跃。 时代版本公布工夫外围概念手动档JDK1.01996-01-23Thread和Runnable手动档JDK1.21998-12-04ThreadLocal、Collections自动档JDK1.5/5.02004-09-30明确Java内存模型、引入并发包自动档JDK1.6/6.02006-12-11synchronized优化自动档JDK1.7/7.02011-07-28Fork/Join框架自动档JDK1.8/8.02014-03-18CompletableFuture、Stream自动档JDK1.9/9.02014-09-08改善锁争用机制自动档JDK102018-03-21线程-部分管控自动档JDK152020-09-15禁用和废除偏差锁主动驾驶JDK192022-09-22虚构线程 手动档时代JDK1.4及以下笔者称之为多线程“手动档”的时代,也叫原生多线程时代。线程的操作还绝对原生,没有线程池可用。研发人员必须手写工具防止频繁创立线程造成资源节约,手动对共享资源加锁。也正是这个时代酝酿了许多优良的多线程框架,最有名的被JDK5.0驳回了。 JDK 1.0 Thread和Runnable1996年1月的JDK1.0版本,从一开始就确立了Java最根底的线程模型,并且,这样的线程模型再后续的修修补补中,并未产生实质性的变更,能够说是一个具备传承性的良好设计。抢占式和合作式是两种常见的过程/线程调度形式,操作系统非常适合应用抢占式形式来调度它的过程,它给不同的过程调配工夫片,对于长期无响应的过程,它有能力剥夺它的资源,甚至将其强行进行。采纳合作式的形式,须要过程盲目、被动地开释资源,在这种调度形式下,可能一个执行工夫很长的线程使得其余所有须要CPU的线程”饿死”。Java hotspot虚拟机的调度形式为抢占式调用,因而Java语言一开始就采纳抢占式线程调度的形式。JDK 1.0中创立线程的形式次要是继承Thread类或实现Runnable接口,通过对象实例的start办法启动线程,须要并行处理的代码放在run办法中,线程间的合作通信采纳简略粗犷的stop/resume/suspend这样的办法。 如何解释stop/resume/suspend的概念呢?就是主线程能够间接调用子线程的终止,暂停,持续办法。如果你小时候用过随身听,下面有三个按键,终止,暂停,持续。设想一下你正在同时听3个随身听,三个随身听就是三个子线程,你就是主线程,你能够随便管制这三个设施的启停。 这一套机制有个致命的问题,就是容易产生死锁,起因在于当线程A锁定了某个资源,还未开释时,被主线程暂停了(suspend办法并不会开释锁),此时线程B如果想占有这个资源,只能期待线程A执行持续操作(resume)后开释资源,否则将永远得不到,产生死锁。 JDK 1.2粗犷的stop/resume/suspend机制在这个版本被禁止应用了,转而采纳wait/notify/sleep这样的多条线程配合口头的形式。值得一提的是,在这个版本中,原子对象AtomicityXXX曾经设计好了,次要是解决i++非原子性的问题。ThreadLocal和Collections的退出减少了多线程应用的姿态,因为这两项技术,笔者称它为Java的涡轮增压时代。 ThreadLocalThreadLocal是一种采纳无锁的形式实现多线程共享线程不平安对象的计划。它并不能解决“银行账户或库存减少、扣减”这类问题,它善于将具备“工具”属性的类,通过复本的形式平安的执行“工具”办法。典型的如SimpleDateFormat、库连贯等。值得一提的是它的设计十分奇妙,想像一下如果让你设计,个别的简略思路是:在ThreadLocal里保护一个全局线程平安的Map,key为线程,value为共享对象。这样设计有个弊病就是内存泄露问题,因为该Map会随着越来越多的线程退出而有限收缩,如果要解决内容泄露,必须在线程完结时清理该Map,这又得强化GC能力了,显然投入产出比不适合。于是,ThreadLocal就被设计成Map不禁ThreadLocal持有,而是由Thread自身持有。key为ThreadLocal变量,value为值。每个Thread将所用到的ThreadLoacl都放于其中(当然此设计还有其它衍生问题在此不表,感兴趣的同学能够自行搜寻)。 CollectionsCollections工具类在这个版本被设计进去了,它包装了一些线程平安汇合如SynchronizedList。在那个只有Hashtable、Vector、Stack等线程平安汇合的年代,它的呈现也是具备时代意义的。Collections工具的根本思维是我帮你将线程不平安的汇合包装成线程平安的,这样你原有代码降级革新不用花很多工夫,只须要在汇合创立的时候用我提供办法初始化汇合即可。比拟像汽车的涡轮增压技术,在发动机排量不变的状况下,减少发动机的功率和扭矩。Java的涡轮增压时代到来了^_^ 自动档时代JDK 5.0引入并发包Doug Lea,中文名为道格·利。是美国的一个大学老师,大神级的人物,J.U.C就是出自他之手。JDK1.5之前,咱们控制程序并发拜访同步代码只能应用synchronized,那个时候synchronized的性能还没优化好,性能并不好,控制线程也只能应用Object的wait和notify办法。这个时候Doug Lea给JCP提交了JSR-166的提案,在提交JSR-166之前,Doug Lea曾经应用了相似J.U.C包性能的代码曾经三年多了,这些代码就是J.U.C的原型。 J.U.C提供了原子化对象、锁及工具套装、线程池、线程平安容器等几大类工具。研发人员可灵便的应用任意能力搭建本人的产品,进可应用ReentrantLock搭建底层框架,退可间接应用现成的工具或容器进行业务代码编写。站在历史的角度去看,J.U.C在2004年毫无争议能够称为“尖端科技产品”。为Java的推广立下了悍马功绩。Java的自动档时代到来了,就好比自动档的汽车升高司机的门槛一样,J.U.C大大降低了程序员应用多线程的门槛。这是个创始了一个时代的产品。 当然J.U.C同样存在一结瑕疵: CPU开销大:如果自旋CAS长时间地不胜利,则会给CPU带来十分大的开销。 解决方案:在JUC中有些中央就限度了CAS自旋的次数,例如BlockingQueue的SynchronousQueue。 ABA问题:如果一个值原来是A,变成了B,而后又变成了A,在CAS查看时会发现没有扭转,但理论它曾经扭转,这就是ABA问题。大部分状况下ABA问题不会影响程序并发的正确性。 解决方案:每个变量都加上一个版本号,每次扭转时加1,即A —> B —> A,变成1A —> 2B —> 3A。Java提供了AtomicStampedReference来解决。AtomicStampedReference通过包装[E,Integer]的元组来对对象标记版本戳(stamp),从而防止ABA问题。 只能保障一个共享变量原子操作:CAS机制所保障的只是一个变量的原子性操作,而不能保障整个代码块的原子性。 解决方案:比方须要保障3个变量独特进行原子性的更新,就不得不应用Synchronized了。还能够思考应用AtomicReference来包装多个变量,通过这种形式来解决多个共享变量的状况。 明确Java内存模型此版本的JDK从新明确了Java内存模型,在这之前,常见的内存模型包含间断一致性内存模型和后行产生模型。 对于间断一致性模型来说,程序执行的程序和代码上显示的程序是完全一致的。这对于古代多核,并且指令执行优化的CPU来说,是很难保障的。而且,程序一致性的保障将JVM对代码的运行期优化重大限制住了。 然而此版本JSR 133标准指定的后行产生(Happens-before)使得执行指令的程序变得灵便: 在同一个线程外面,依照代码执行的程序(也就是代码语义的程序),前一个操作先于前面一个操作产生 对一个monitor对象的解锁操作先于后续对同一个monitor对象的锁操作 对volatile字段的写操作先于前面的对此字段的读操作 对线程的start操作(调用线程对象的start()办法)先于这个线程的其余任何操作 一个线程中所有的操作先于其余任何线程在此线程上调用 join()办法 如果A操作优先于B,B操作优先于C,那么A操作优先于C而在内存调配上,将每个线程各自的工作内存从主存中独立进去,更是给JVM大量的空间来优化线程内指令的执行。主存中的变量能够被拷贝到线程的工作内存中去独自执行,在执行完结后,后果能够在某个工夫刷回主存: 然而,怎么来保障各个线程之间数据的一致性?JLS(Java Language Specification)给的方法就是,默认状况下,不能保障任意时刻的数据一致性,然而通过对synchronized、volatile和final这几个语义被加强的关键字的应用,能够做到数据一致性。 JDK 6.0 synchronized优化作为“共和国长子”synchronized关键字,在5.0版本被ReentrantLock压过了风头。这个版本必须要扳回一局,因而JDK 6.0对锁做了一些优化,比方锁自旋、锁打消、锁合并、轻量级锁、所偏差等。本次优化是对“精细化治理”这个理念的一次诠释。没优化之前被synchronized加锁的对象只有两个状态:无锁,有锁(重量级锁)。优化后锁一共存在4种状态,级别从低到高顺次是:无锁、偏差锁、轻量级锁、重量级锁。这几个状态随着竞争的状况逐步降级,然而不能降级,目标是为了进步获取锁和开释锁的效率(笔者认为其实是太简单了,JVM研发人员望而生畏了)。 这一次优化让synchronized扬眉吐气,自此再也不容许他人说它的性能比ReentrantLock差了。但好戏还在后头,偏差锁在JDK 15被废除了(─.─||)。笔者认为synchronized吃亏在了它只是个关键字,JVM负责它底层的动作,到底应用程序加锁的时候什么样的姿态难受,得靠JVM“猜”。ReentrantLock就不同了,它将这件事间接交给程序员去解决了,你心愿偏心那就用偏心锁,你心愿你的不偏心,那你就用非偏心锁。设计层面算是一种偷懒,但同时也是一种灵便。 ...

March 3, 2023 · 2 min · jiezi

关于java:三天吃透计算机网络八股文

本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址:https://github.com/Tyson0314/Java-learning 网络分层构造计算机网络体系大抵分为三种,OSI七层模型、TCP/IP四层模型和五层模型。个别面试的时候考查比拟多的是五层模型。 五层模型:应用层、传输层、网络层、数据链路层、物理层。 应用层:为应用程序提供交互服务。在互联网中的应用层协定很多,如域名零碎DNS、HTTP协定、SMTP协定等。传输层:负责向两台主机过程之间的通信提供数据传输服务。传输层的协定次要有传输控制协议TCP和用户数据协定UDP。网络层:抉择适合的路由和替换结点,确保数据及时传送。次要包含IP协定。数据链路层:在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。物理层:实现相邻节点间比特流的通明传输,尽可能屏蔽传输介质和物理设施的差别。ISO七层模型是国际标准化组织(International Organization for Standardization)制订的一个用于计算机或通信零碎间互联的规范体系。 应用层:网络服务与最终用户的一个接口,常见的协定有:HTTP FTP SMTP SNMP DNS.表示层:数据的示意、平安、压缩。,确保一个零碎的应用层所发送的信息能够被另一个零碎的应用层读取。会话层:建设、治理、终止会话,对应主机过程,指本地主机与近程主机正在进行的会话.传输层:定义传输数据的协定端口号,以及流控和过错校验,协定有TCP UDP.网络层:进行逻辑地址寻址,实现不同网络之间的门路抉择,协定有ICMP IGMP IP等.数据链路层:在物理层提供比特流服务的根底上,建设相邻结点之间的数据链路。物理层:建设、保护、断开物理连贯。TCP/IP 四层模型 应用层:对应于OSI参考模型的(应用层、表示层、会话层)。传输层: 对应OSI的传输层,为应用层实体提供端到端的通信性能,保障了数据包的程序传送及数据的完整性。网际层:对应于OSI参考模型的网络层,次要解决主机到主机的通信问题。网络接口层:与OSI参考模型的数据链路层、物理层对应。三次握手假如发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是CLOSED。 第一次握手:客户端向服务端发动建设连贯申请,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中蕴含标记位SYN=1,序列号seq=x。第一次握手前客户端的状态为CLOSE,第一次握手后客户端的状态为SYN-SENT。此时服务端的状态为LISTEN。第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y,而后给客户端回复一段报文,其中包含标记位SYN=1,ACK=1,序列号seq=y,确认号ack=x+1。第二次握手前服务端的状态为LISTEN,第二次握手后服务端的状态为SYN-RCVD,此时客户端的状态为SYN-SENT。(其中SYN=1示意要和客户端建设一个连贯,ACK=1示意确认序号无效)第三次握手:客户端收到服务端发来的报文后,会再向服务端发送报文,其中蕴含标记位ACK=1,序列号seq=x+1,确认号ack=y+1。第三次握手前客户端的状态为SYN-SENT,第三次握手后客户端和服务端的状态都为ESTABLISHED。此时连贯建设实现。两次握手能够吗?之所以须要第三次握手,次要为了避免已生效的连贯申请报文段忽然又传输到了服务端,导致产生问题。 比方客户端A收回连贯申请,可能因为网络阻塞起因,A没有收到确认报文,于是A再重传一次连贯申请。而后连贯胜利,期待数据传输结束后,就开释了连贯。而后A收回的第一个连贯申请等到连贯开释当前的某个工夫才达到服务端B,此时B误认为A又收回一次新的连贯申请,于是就向A收回确认报文段。如果不采纳三次握手,只有B收回确认,就建设新的连贯了,此时A不会响应B的确认且不发送数据,则B始终期待A发送数据,浪费资源。四次挥手 A的利用过程先向其TCP收回连贯开释报文段(FIN=1,seq=u),并进行再发送数据,被动敞开TCP连贯,进入FIN-WAIT-1(终止期待1)状态,期待B的确认。B收到连贯开释报文段后即收回确认报文段(ACK=1,ack=u+1,seq=v),B进入CLOSE-WAIT(敞开期待)状态,此时的TCP处于半敞开状态,A到B的连贯开释。A收到B的确认后,进入FIN-WAIT-2(终止期待2)状态,期待B收回的连贯开释报文段。B发送完数据,就会收回连贯开释报文段(FIN=1,ACK=1,seq=w,ack=u+1),B进入LAST-ACK(最初确认)状态,期待A的确认。A收到B的连贯开释报文段后,对此收回确认报文段(ACK=1,seq=u+1,ack=w+1),A进入TIME-WAIT(工夫期待)状态。此时TCP未开释掉,须要通过工夫期待计时器设置的工夫2MSL(最大报文段生存工夫)后,A才进入CLOSED状态。B收到A收回的确认报文段后敞开连贯,若没收到A收回的确认报文段,B就会重传连贯开释报文段。第四次挥手为什么要期待2MSL?保障A发送的最初一个ACK报文段可能达到B。这个ACK报文段有可能失落,B收不到这个确认报文,就会超时重传连贯开释报文段,而后A能够在2MSL工夫内收到这个重传的连贯开释报文段,接着A重传一次确认,重新启动2MSL计时器,最初A和B都进入到CLOSED状态,若A在TIME-WAIT状态不期待一段时间,而是发送完ACK报文段后立刻开释连贯,则无奈收到B重传的连贯开释报文段,所以不会再发送一次确认报文段,B就无奈失常进入到CLOSED状态。避免已生效的连贯申请报文段呈现在本连贯中。A在发送完最初一个ACK报文段后,再通过2MSL,就能够使这个连贯所产生的所有报文段都从网络中隐没,使下一个新的连贯中不会呈现旧的连贯申请报文段。为什么是四次挥手?因为当Server端收到Client端的SYN连贯申请报文后,能够间接发送SYN+ACK报文。然而在敞开连贯时,当Server端收到Client端收回的连贯开释报文时,很可能并不会立刻敞开SOCKET,所以Server端先回复一个ACK报文,通知Client端我收到你的连贯开释报文了。只有等到Server端所有的报文都发送完了,这时Server端能力发送连贯开释报文,之后两边才会真正的断开连接。故须要四次挥手。 TCP有哪些特点?TCP是面向连贯的运输层协定。点对点,每一条TCP连贯只能有两个端点。TCP提供牢靠交付的服务。TCP提供全双工通信。面向字节流。说说TCP报文首部有哪些字段,其作用又别离是什么? 16位端口号:源端口号,主机该报文段是来自哪里;指标端口号,要传给哪个下层协定或应用程序32位序号:一次TCP通信(从TCP连贯建设到断开)过程中某一个传输方向上的字节流的每个字节的编号。32位确认号:用作对另一方发送的tcp报文段的响应。其值是收到的TCP报文段的序号值加1。4位头部长度:示意tcp头部有多少个32bit字(4字节)。因为4位最大能标识15,所以TCP头部最长是60字节。6位标记位:URG(紧急指针是否无效),ACk(示意确认号是否无效),PSH(缓冲区尚未填满),RST(示意要求对方从新建设连贯),SYN(建设连贯音讯标记接),FIN(示意告知对方本端要敞开连贯了)16位窗口大小:是TCP流量管制的一个伎俩。这里说的窗口,指的是接管通告窗口。它通知对方本端的TCP接收缓冲区还能包容多少字节的数据,这样对方就能够管制发送数据的速度。16位校验和:由发送端填充,接收端对TCP报文段执行CRC算法以测验TCP报文段在传输过程中是否损坏。留神,这个校验不仅包含TCP头部,也包含数据局部。这也是TCP牢靠传输的一个重要保障。16位紧急指针:一个正的偏移量。它和序号字段的值相加示意最初一个紧急数据的下一字节的序号。因而,确切地说,这个字段是紧急指针绝对以后序号的偏移,无妨称之为紧急偏移。TCP的紧急指针是发送端向接收端发送紧急数据的办法。TCP和UDP的区别?TCP面向连贯;UDP是无连贯的,即发送数据之前不须要建设连贯。TCP提供牢靠的服务;UDP不保障牢靠交付。TCP面向字节流,把数据看成一连串无构造的字节流;UDP是面向报文的。TCP有拥塞管制;UDP没有拥塞管制,因而网络呈现拥塞不会使源主机的发送速率升高(对实时利用很有用,如实时视频会议等)。每一条TCP连贯只能是点到点的;UDP反对一对一、一对多、多对一和多对多的通信形式。TCP首部开销20字节;UDP的首部开销小,只有8个字节。TCP 和 UDP 别离对应的常见应用层协定有哪些?基于TCP的应用层协定有:HTTP、FTP、SMTP、TELNET、SSH HTTP:HyperText Transfer Protocol(超文本传输协定),默认端口80FTP: File Transfer Protocol (文件传输协定), 默认端口(20用于传输数据,21用于传输管制信息)SMTP: Simple Mail Transfer Protocol (简略邮件传输协定) ,默认端口25TELNET: Teletype over the Network (网络电传), 默认端口23SSH:Secure Shell(平安外壳协定),默认端口 22基于UDP的应用层协定:DNS、TFTP、SNMP DNS : Domain Name Service (域名服务),默认端口 53TFTP: Trivial File Transfer Protocol (简略文件传输协定),默认端口69SNMP:Simple Network Management Protocol(简略网络管理协定),通过UDP端口161接管,只有Trap信息采纳UDP端口162。TCP的粘包和拆包TCP是面向流,没有界线的一串数据。TCP底层并不理解下层业务数据的具体含意,它会依据TCP缓冲区的理论状况进行包的划分,所以在业务上认为,一个残缺的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。 为什么会产生粘包和拆包呢? ...

March 3, 2023 · 3 min · jiezi

关于java:SpringData-MongoDB小试牛刀

原始博文链接 之前始终想试试MongoDB但总是没什么机会用,关系型数据库还是大多数利用场景的首选。最近有机会须要做个评测零碎,其中波及蕴含大量选项的不同品种的表单解决,同时规模并不大,遂思考一番后决定用MongoDB来作为存储后端,正好尝尝鲜,也能加重点CRUD的干燥感。本文次要进行一些根本介绍以及展现应用示例,总结了一些应用的感触和体验。 MongoDB简介MongoDB算是相当驰名的一款开源NoSQL数据库了,它也曾经有十多年的历史,截止到目前(文章公布)MongoDB也曾经来到了4.4版本,以MongoDB数据库为根底成立的MongoDB公司目前除了MongoDB Server之外还提供了MongoDB Atlas(SaaS云服务数据库)、Charts(可视化图表)、Compass(数据库GUI工具)、Connectors(连接器)等各种配套服务和工具。有趣味能够看一看MongoDB的倒退历史(英),作为饭后读物非常不错,能够看到一款开源数据库的成长过程。 MongoDB说到底是一款开源的由C++编写的文档数据库,面向用户展示的构造与一般RDB也比拟类似,其外围概念根本都能在RDB中找到对应的内容: MongoRDB阐明databasedatabase数据库collectiontable数据汇合 - 数据表documentrow文档 - 数据记录fieldcolumn字段 - 数据列indexindex索引其中最外围的便是文档,它代表了MongoDB中的一个数据记录。MongoDB的文档构造如下图所示,很显著相似于JSON,能够蕴含数组和其余文档。 无schema的构造(能够有)配合丰盛灵便的查问语句使得MongoDB相较于传统RDB具备很大的灵活性。 装置好MongoDB而后装个GUI(能够抉择MongoDB Compass),而后关上GUI工具连贯到数据库操作一番插入几条数据根本就可能明确其次要的能力。装置办法这里不赘述,因为顺手一查就有,不过装置时须要留神的是MongoDB原生反对分布式架构,包含分片、主备等等,留神装置设置。 SpringData MongoDB理解了MongoDB之后就能够将其利用到理论应用场景中,而作为独立的存储工具,又必然波及到连贯交互,因而MongoDB提供了各种语言的Driver,其中也蕴含了Java。随着利用复杂度减少,各种工具提供了更高阶的形象或封装来简化对DB的操作以提高效率,SpringData便是其中之一,它作为Spring对于数据存储层拜访的对立根底工具,也提供了针对MongoDB的实现。 BSONBSON是MongoDB数据存储和网络传输的次要格局,它意为Binary JSON,它的字面意思间接示意了它被创造进去的次要起因。BSON的二进制构造蕴含了类型和长度信息以获取更块的解析速度和更高效的查问性能,此外BSON扩大反对了非JSON原生的数据类型如日期、二进制数据等。得益于此,所有可能在JSON中表白的数据都可能间接在MongoDB中存储、读取。 此外MongoDB的查问同样采纳类JSON的格局来形容,集体感觉有一种谐和、自描述的感觉。配合各种操作符,表达能力很强且有很不错的可浏览性。这个个性也在Driver API中有所体现,以Java角度来说,数据和查问都能够应用同一种类型(org.bson.Document)来示意,十分优雅。举一个简略的查问条件例子: { status: "A", $or: [ { price: { $lt: 30 } }, { item: /^p/ } ]} 上述形容了查问抉择status为"A"且price大于30或item以字符p结尾(反对正则表达式)的所有文档。 Mongo Java DriverDriver作为与MongoDB通信交互的根底,Spring Data所进行的操作最初还是会调用Driver提供的办法来发送申请,这些额定的封装操作最为重要的几点目标是查问的构建以及返回值到对象的映射,其实这也是ORM框架所做的次要内容。想较于JDBC只提供了对SQL字符串的提交解决,Mongo的Java Driver要丰盛许多,间接提供了对象的解码编码以及函数式查问构建,这也意味着只用原生Driver的API也能够很好地适配面向对象以及便捷查问。 Spring DataSpring Data作为Spring对于数据存储层拜访的对立根底工具,也提供了针对MongoDB的封装。只管原生的Java Mongo Driver曾经提供了一些要害的能力,然而Spring Data并没有应用这些内容,查问的构建以及对象转换依然是Spring本人的一套。Spring Data MongoDB看起来与Driver原生提供的性能有些反复,然而毕竟Spring Data个性更加丰盛,同时提供了对立的Data Repository拜访形式。不过应用Spring Data这一套也并不是没有毛病,Spring的这层包装也会显得比拟重,若不是在Spring我的项目中应用,采纳原生Driver未尝不是好的抉择。 利用示例上面别离对原生Driver和Spring Data MongoDB的应用做一些介绍和示例。 原生Driver原生的Java Driver 4.1版本文档传送门在此,文档很全面,包含了教程、API文档、源码、更新信息等等。这里简述一些次要操作形容一番。 入口类是com.mongodb.client.MongoClient而并不是相似xxxConnection的模式,然而实质上依然是获取连贯对象。很显著Mongo Driver的形象水平自身就比拟高,MongoClient提供了多种创立形式: // direct for localhost:27017MongoClient mongoClient1= MongoClients.create();// specify host and portMongoClient mongoClient2= MongoClients.create( MongoClientSettings.builder() .applyToClusterSettings(builder -> builder.hosts(Arrays.asList(new ServerAddress("hostOne", 27018)))) .build());// from connection stringMongoClient mongoClient = MongoClients.create("mongodb://admin:admin@localhost:27017/?authSource=admin&readPreference=primary&ssl=false");// print databases' namemongoClient.listDatabaseNames().forEach(System.out::println);通常MongoClient作为单例存在即可,通过MongoClient能够获取到com.mongodb.client.MongoDatabase代表MongoDB中的Database,而Database对象又能够获取到具体的Collection即com.mongodb.client.MongoCollection,通过Collection对象来执行对应的增删改查: ...

March 2, 2023 · 3 min · jiezi

关于java:面试官从-MySQL-读取-100w-数据进行处理应该怎么做问倒一大遍

背景大数据量操作的场景大抵如下: 数据迁徙数据导出批量解决数据在理论工作中当指定查问数据过大时,咱们个别应用分页查问的形式一页一页的将数据放到内存解决。但有些状况不须要分页的形式查问数据或分很大一页查问数据时,如果一下子将数据全副加载进去到内存中,很可能会产生OOM(内存溢出);而且查问会很慢,因为框架消耗大量的工夫和内存去把数据库查问的后果封装成咱们想要的对象(实体类)。 举例:在业务零碎须要从 MySQL 数据库里读取 100w 数据行进行解决,应该怎么做? 做法通常如下: 惯例查问: 一次性读取 100w 数据到 JVM 内存中,或者分页读取流式查问: 建设长连贯,利用服务端游标,每次读取一条加载到 JVM 内存(屡次获取,一次一行)游标查问: 和流式一样,通过 fetchSize 参数,管制一次读取多少条数据(屡次获取,一次多行)惯例查问默认状况下,残缺的检索后果集会将其存储在内存中。在大多数状况下,这是最无效的操作形式,并且因为 MySQL 网络协议的设计,因而更易于实现。 举例: 假如单表 100w 数据量,个别会采纳分页的形式查问: @Mapperpublic interface BigDataSearchMapper extends BaseMapper<BigDataSearchEntity> { @Select("SELECT bds.* FROM big_data_search bds ${ew.customSqlSegment} ") Page<BigDataSearchEntity> pageList(@Param("page") Page<BigDataSearchEntity> page, @Param(Constants.WRAPPER) QueryWrapper<BigDataSearchEntity> queryWrapper); }注:该示例应用的 MybatisPlus该形式比较简单,如果在不思考 LIMIT 深分页优化状况下,预计你的数据库服务器就噶皮了,或者你能等上几十分钟或几小时,甚至几天工夫检索数据。 举荐一个开源收费的 Spring Boot 最全教程: https://github.com/javastacks/spring-boot-best-practice流式查问流式查问指的是查问胜利后不是返回一个汇合而是返回一个迭代器,利用每次从迭代器取一条查问后果。流式查问的益处是可能升高内存应用。 如果没有流式查问,咱们想要从数据库取 100w 条记录而又没有足够的内存时,就不得不分页查问,而分页查问效率取决于表设计,如果设计的不好,就无奈执行高效的分页查问。因而流式查问是一个数据库拜访框架必须具备的性能。 MyBatis 中应用流式查问防止数据量过大导致 OOM ,但在流式查问的过程当中,数据库连贯是放弃关上状态的,因而要留神的是: 执行一个流式查问后,数据库拜访框架就不负责敞开数据库连贯了,须要利用在取完数据后本人敞开。必须先读取(或敞开)后果集中的所有行,而后能力对连贯收回任何其余查问,否则将引发异样。MyBatis 流式查问接口MyBatis 提供了一个叫 org.apache.ibatis.cursor.Cursor 的接口类用于流式查问,这个接口继承了 java.io.Closeable 和 java.lang.Iterable 接口,由此可知: ...

March 2, 2023 · 1 min · jiezi

关于java:风控系统就该这么设计万能通用稳的一批建议收藏

作者:wingli\链接:https://juejin.cn/post/7182774381448282172 一、背景1.为什么要做风控?这不得拜产品大佬所赐 目前咱们业务有应用到十分多的AI能力,如ocr辨认、语音测评等,这些能力往往都比拟费钱或者费资源,所以在产品层面也心愿咱们对用户的能力应用次数做肯定的限度,因而风控是必须的! 2.为什么要本人写风控?那么多开源的风控组件,为什么还要写呢?是不是想反复创造轮子呀. 要想答复这个问题,须要先解释下咱们业务须要用到的风控(简称业务风控),与开源常见的风控(简称一般风控)有何区别: 风控类型目标对象规定业务风控实现产品定义的一些限度,达到限度时,有具体的业务流程,如充值vip等比拟复杂多变的,例如针对用户进行风控,也能针对用户+年级进行风控天然日、天然小时等一般风控爱护服务或数据,拦挡异样申请等接口、局部能够加上简略参数个别用得更多的是滑动窗口因而,间接应用开源的一般风控,个别状况下是无奈满足需要的 3.其它要求反对实时调整限度:很多限度值在首次设置的时候,基本上都是拍定的一个值,后续须要调整的可能性是比拟大的,因而可调整并实时失效是必须的 二、思路要实现一个简略的业务风控组件,要做什么工作呢? 1.风控规定的实现a.须要实现的规定:天然日计数天然小时计数天然日+天然小时计数天然日+天然小时计数 这里并不能单纯地串联两个判断,因为如果天然日的断定通过,而天然小时的断定不通过的时候,须要回退,天然日跟天然小时都不能计入本次调用!b.计数形式的抉择:目前能想到的会有: mysql+db事务长久化、记录可溯源、实现起来比拟麻烦,略微“重”了一点redis+lua实现简略,redis的可执行lua脚本的个性也能满足对“事务”的要求mysql/redis+分布式事务须要上锁,实现简单,能做到比拟准确的计数,也就是真正等到代码块执行胜利之后,再去操作计数目前没有很准确技术的要求,代价太大,也没有长久化的需要,因而选用 redis+lua 即可2.调用形式的实现a.常见的做法先定义一个通用的入口 举荐一个开源收费的 Spring Boot 最全教程: https://github.com/javastacks/spring-boot-best-practice//简化版代码@Componentclass DetectManager { fun matchExceptionally(eventId: String, content: String){ //调用规定匹配 val rt = ruleService.match(eventId,content) if (!rt) { throw BaseException(ErrorCode.OPERATION_TOO_FREQUENT) } }}在service中调用该办法 //简化版代码@Serviceclass OcrServiceImpl : OcrService { @Autowired private lateinit var detectManager: DetectManager /** * 提交ocr工作 * 须要依据用户id来做次数限度 */ override fun submitOcrTask(userId: String, imageUrl: String): String { detectManager.matchExceptionally("ocr", userId) //do ocr }}有没有更优雅一点的办法呢? 用注解可能会更好一点(也比拟有争议其实,这边先反对实现) 因为传入的 content 是跟业务关联的,所以须要通过Spel来将参数形成对应的content三、具体实现1.风控计数规定实现a.天然日/天然小时天然日/天然小时能够共用一套lua脚本,因为它们只有key不同,脚本如下: ...

March 2, 2023 · 2 min · jiezi

关于java:从菜鸟程序员到高级架构师竟然是因为这个字final

final实现原理 简介final关键字,理论的含意就一句话,不可扭转。什么是不可扭转?就是初始化实现之后就不能再做任何的批改,润饰成员变量的时候,成员变量变成一个常数;润饰办法的时候,办法不容许被重写;润饰类的时候,类不容许被继承;润饰参数列表的时候,入参的对象也是不能够扭转。这个就是不可变,无论是援用新的对象,重写还是继承,都是扭转的办法,而final就是把这个变更的路给堵死 用法final润饰变量final成员变量示意常量,只能被赋值一次,赋值后值不再扭转(final要求地址值不能扭转)当final润饰一个根本数据类型时,示意该根本数据类型的值一旦在初始化后便不能发生变化;如果final润饰一个援用类型时,则在对其初始化之后便不能再让其指向其余对象了,但该援用所指向的对象的内容是能够发生变化的。实质上是一回事,因为援用的值是一个地址,final要求值,即地址的值不发生变化。final润饰一个成员变量(属性),必须要显示初始化。这里有两种初始化形式。 一种是在变量申明的时候初始化。第二种办法是在申明变量的时候不赋初值,然而要在这个变量所在的类的所有的构造函数中对这个变量赋初值。final润饰办法应用final办法的起因有两个。第一个起因是把办法锁定,以防任何继承类批改它的含意,不能被重写;第二个起因是效率,final办法比非final办法要快,因为在编译的时候曾经动态绑定了,不须要在运行时再动静绑定。注:类的private办法会隐式地被指定为final办法 final润饰类当用final润饰一个类时,表明这个类不能被继承。final类中的成员变量能够依据须要设为final,然而要留神final类中的所有成员办法都会被隐式地指定为final办法。 在应用final润饰类的时候,要留神审慎抉择,除非这个类真的在当前不会用来继承或者出于平安的思考,尽量不要将类设计为final类。 final关键字的益处final关键字进步了性能。JVM和Java利用都会缓存final变量。final变量能够平安的在多线程环境下进行共享,而不须要额定的同步开销。应用final关键字,JVM会对办法、变量及类进行优化。注意事项final关键字能够用于成员变量、本地变量、办法以及类。final成员变量必须在申明的时候初始化或者在结构器中初始化,否则就会报编译谬误。你不可能对final变量再次赋值。本地变量必须在申明时赋值。在匿名类中所有变量都必须是final变量。final办法不能被重写。final类不能被继承。final关键字不同于finally关键字,后者用于异样解决。final关键字容易与finalize()办法搞混,后者是在Object类中定义的办法,是在垃圾回收之前被JVM调用的办法。接口中申明的所有变量自身是final的。final和abstract这两个关键字是反相干的,final类就不可能是abstract的。final办法在编译阶段绑定,称为动态绑定(static binding)。没有在申明时初始化final变量的称为空白final变量(blank final variable),它们必须在结构器中初始化,或者调用this()初始化。不这么做的话,编译器会报错“final变量(变量名)须要进行初始化”。将类、办法、变量申明为final可能进步性能,这样JVM就有机会进行预计,而后优化。依照Java代码常规,final变量就是常量,而且通常常量名要大写。对于汇合对象申明为final指的是援用不能被更改,然而你能够向其中减少,删除或者扭转内容。原理内存语义写内存语义能够确保在对象的援用为任意线程可见之前,final 域曾经被初始化过了。 读内存语义能够确保如果对象的援用不为 null,则阐明 final 域曾经被初始化过了。 总之,final 域的内存语义提供了初始化平安保障。 写内存语义:在构造函数内对一个 final 域的写入,与随后将对象援用赋值给援用变量,这两个操作不能重排序。读内存语义:首次读一个蕴含 final 域的对象的援用,与随后首次读这个 final 域,这两个操作不能重排序。写 final 域的重排序规定写 final 域的重排序规定禁止把 final 域的写重排序到构造函数之外。这个规定的实现蕴含上面 2 个方面:JMM 禁止编译器把 final 域的写重排序到构造函数之外。编译器会在 final 域的写之后,构造函数 return 之前,插入一个 StoreStore 屏障。这个屏障禁止处理器把 final 域的写重排序到构造函数之外。当初让咱们剖析 writer () 办法。writer () 办法只蕴含一行代码:finalExample = new FinalExample ()。这行代码蕴含两个步骤:结构一个 FinalExample 类型的对象;把这个对象的援用赋值给援用变量 obj。假如线程 B 读对象援用与读对象的成员域之间没有重排序(马上会阐明为什么须要这个假如),下图是一种可能的执行时序: 在上图中,写一般域的操作被编译器重排序到了构造函数之外,读线程 B 谬误的读取了一般变量 i 初始化之前的值。而写 final 域的操作,被写 final 域的重排序规定“限定”在了构造函数之内,读线程 B 正确的读取了 final 变量初始化之后的值。 ...

March 2, 2023 · 3 min · jiezi

关于java:JAVA自定义类加载器实现类隔离

一、背景某服务须要连贯操作多种组件(每种组件可能有多个版本),如kafka、mongodb、es、mysql等等,并且后续须要适配更多的组件。 次要难点:连贯操作多组件多版本,且同种组件的不同版本所依赖的jar包可能不一样,操作源码也可能产生扭转,我的项目无奈间接依赖jar包,会产生类抵触 二、解决思路因为每种组件的不同版本所依赖的jar包不同,咱们能够借鉴tomcat的实现形式,通过自定义类加载器突破双亲委派机制来实现类隔离,从而达到操作多组件多版本的目标。 2.1 创立依赖所在目录针对每一种组件咱们创立一个目录,比方/data/kafka、/data/mongodb、/data/es等,且每种组件的不同版本创立对应的子目录,比方/data/kafka/0.10、/data/kafka/0.11,目录构造如下 | ----/data| --------/kafka| ------------/0.10| ------------/0.11| --------/mysql| ------------/5.7| ------------/8.0| ...把每种组件不同版本对应的依赖包放在各个子目录上面。 2.2 定义操作接口在common公共模块中定义一个接口AbstractOperator,该接口定义一些通用办法,如下: public interface Operator { /** * 测试连贯 * @param connectionInfo * @return */ boolean testConnection(String connectionInfo); /** * 获取组件版本 * @return */ String getVersion(String connectionInfo);}再定义各种组件的接口,如KafkaOperator、MysqlOperator等,使其继承该通用接口。组件接口外部蕴含一些组件本身的操作,如KafkaOperator中定义了getTopics、createTopic、deleteTopic等办法。代码如下: public interface KafkaOperator extends Operator{ /** * 获取topic列表 * @param connectionInfo * @return */ List<String> getTopics(String connectionInfo); /** * 创立topic * @param connectionInfo * @param topic * @return */ boolean createTopic(String connectionInfo, String topic); /** * 删除topic * @param connectionInfo * @param topic * @return */ boolean deleteTopic(String connectionInfo, String topic);}2.3 编写并构建业务包大抵步骤如下: ...

March 1, 2023 · 2 min · jiezi

关于java:Runable和Callable的区别你必须要搞清楚Thread以及FutureTask

Runable与Callable的区别,据说是高频面试题,什么样的答案才会让面试官称心呢? 所有java程序员都晓得的答案是基于: public interface Runnable { public abstract void run();}public interface Callable<V> { V call() throws Exception;}从Runable和Callable接口的定义咱们就能晓得的是: Runable接口的run办法不返回后果,不容许他的实现类抛出异样。 Callable接口的call办法能够返回后果,并且容许抛出异样。 再和Thread关联一下,能够补充: 新创建一个线程Thread执行Runable能够实现异步调用,调用线程在创立新线程执行Runable工作之后能够立刻取得执行权,不影响后续工作的执行Callable能够被封装在FutureTask(Runable的实现类)中从而被Thread调用,这种状况下调用线程能够通过FutureTask.get()办法获取到Callable接口的call办法的返回值,然而FutureTask.get()办法是阻塞调用,直到线程执行实现才会有后果返回,所以调用线程如果执行了FutureTask.get()的话,后续工作就必须期待如果单从这个问题来讲,答案应该就差不多残缺了。然而如果面试官老爷还想理解细节,那咱们就必须要对FutureTask甚至Executor做一个深刻的理解。 其实咱们并不是为了答复这个面试问题才去钻研这部分内容的,而是因为接下来筹备认真学习、钻研一下线程池、连接池等池化技术,而FutureTask、Executor是线程池技术的根底,所以咱们就须要首先搞清楚FutureTask和Executor的底层原理,顺道兴许正好能把这个高频java根底面试题答复分明。 进入正题。明天的内容包含: Thread:概念性的简略阐明,不波及启动、进行、中断、挂起等等状态转换机制FutureTask源码剖析Thread简要阐明Java程序是以线程来运行的,JVM容许一个java利用启动多个线程,当咱们的程序以main办法启动之后,其实JVM就启动了一个线程来执行程序。 每一个java thread都有优先级,在资源抢夺过程中高优先级线程比低优先级线程更容易取得执行权。线程能够被标注为是否为守护线程,默认状况下,新创建的线程和创立他的线程领有雷同的优先级,以后仅当创立线程是守护线程的时候,被创立的新线程能力是守护线程。 JVM启动的时候,通常会创立一个非守护线程(主线程),主线程会继续运行直到: Runtime的exit办法执行,或者利用抛出的异样始终被传递到主线程的run办法所有非守护线程执行结束:run办法失常执行实现或捕捉到异样有两种创立线程的形式: 创立一个Thread类的子类并笼罩他的run办法,而后调用子类对象的start办法,最终子类的run办法会被调用执行创立Runable接口的实现类,作为参数传递给Thread的结构器创立Thread并执行start办法,最终Runable实现类的run办法会被调用执行常见的是第2种办法。 FutureTask先看一下FutureTask的类图: 第一点须要晓得的是,FutureTask是Runable接口的实现。 同时他还实现了Future接口,所以咱们须要首先简略理解一下Future,看一下JavaDoc: 体现异步工作的执行后果,提供办法查看工作是否实现、期待异步工作执行实现、并获取执行后果。执行后果只能通过get办法获取,只有工作实现后能力获取到,否则会阻塞期待工作实现后返回后果。通过cancel办法能够勾销工作,Future还提供了一些其余办法能够帮忙获取到工作是失常完结、还是被勾销掉了,一旦工作执行实现,则不再容许勾销。如果你只是想通过Future来实现工作能够被勾销这一个性、而并不在意工作的返回后果,让Future的工作返回null即可。所以咱们晓得Future是为了获取到异步工作的执行后果而生的。 FutureTask的工作FutureTask蕴含一个叫callable的Callable成员变量,就是FutureTask的底层工作,咱们首先看一下这个底层工作是怎么被创立或者初始化的。 先来看FutureTask对象的创立,FutureTask提供了两个构造方法: public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }其中一个构造方法传入Callable对象,间接赋值给成员变量callable,设置state=NEW,简单明了。 ...

March 1, 2023 · 3 min · jiezi

关于java:如何在ovirt中使用GWT框架为页面新增组件

如何在ovirt中应用GWT框架为页面新增组件Ovirt中的GWT框架(GWTP)参考 ovirt gwt 代码剖析如何要在GWT页面新增或者批改组件?批改、新增、删除菜单能够参考文档 减少/批改左侧导航定位页面 ovirt中的页面明规定,ovirt页面 以集群为例集群列表页面 MainClusterView.java(V) 相似页面为 MainNetworkView.java MainHostView.javatab页面 SubTabClusterGeneralView.java 相似页面为SubTabHostGeneralView.java新建、删除、批改等各种按钮监听页面 ClusterPopupView.java 相似页面为 HostPopupView.java 要想找到这些页面能够通过该页面名称在PresenterModule.java页面中进行查问,PresenterModule 中定义了每个 tab、popubview 的 M、V、P 三者之间的关系。批改页面 定位页面后,能够对页面进行操作,比方新增、批改、暗藏某个组件。留神如果view页面有对应的xml页面,批改时须要将两个文件内容同步。 以下以在新建、编辑虚构网卡配置集页面中新增平安组下拉列表为例 依照以上办法对页面进行定位,找到须要批改页面 VnicProfilePopupView.java 且能够发现VnicProfilePopupView.java存在对应VnicProfilePopupView.ui.xml中存在各种援用在页面中新增平安组单选组件VnicProfilePopupView.ui.xml 在VnicProfilePopupView.ui.xml中存在各种援用 <ui:UiBinder<!-- GWT组件 提供能够援用的组件(GWT原生、GWTbootstrap3、ovirt原生) -->xmlns:ui="urn:ui:com.google.gwt.uibinder"xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:d="urn:import:org.ovirt.engine.ui.common.widget.dialog"xmlns:e="urn:import:org.ovirt.engine.ui.common.widget.editor"xmlns:ge="urn:import:org.ovirt.engine.ui.common.widget.editor.generic"xmlns:k="urn:import:org.ovirt.engine.ui.common.widget.form.key_value"xmlns:b="urn:import:org.gwtbootstrap3.client.ui"><!-- 援用利用常量,个别设置组件名称 --><ui:with field='constants' type='org.ovirt.engine.ui.webadmin.ApplicationConstants' />倡议应用原生,及框架中的已知简略组件,能够在网上查问文档进行参考 <b:Row> <e:ListModelListBoxEditor ui:field="securityGroupsEditor" label="{constants.safeGroup}" usePatternFly="true" labelColSize="SM_6" widgetColSize="SM_6" /> </b:Row><e />: 组件所在库 ListModelListBoxEditor:组件名称 ui:field="securityGroupsEditor": 该参数须要和view中保留雷同 label="{constants.safeGroup}" : 组件显示名称 labelColSize="SM_6" widgetColSize="SM_6" : 组件款式 还能够在xml中新增css款式 <ui:style> .firstRow { padding-top: 15px; } .hide_ext { display: none; }</ui:style><b:Row addStyleNames="{style.hide_ext}"> <b:Column size="SM_12" > <g:Label text="{constants.customPropertiesVnicProfile}" /> </b:Column></b:Row>申明的css能够应用addStyleNames="{style.hide_ext}"增加到组件上VnicProfilePopupView.java 减少名称为xml中ui:field值的参数,类型对应所应用的的组件 ...

March 1, 2023 · 2 min · jiezi

关于java:为什么95的Java程序员人都是用不好Synchronized

Synchronized锁优化jdk1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁打消、锁粗化、偏差锁、轻量级锁等技术来缩小锁操作的开销。锁次要存在四中状态,顺次是:无锁-> 偏差锁 -> 轻量级锁 -> 重量级锁,他们会随着竞争的强烈而逐步降级。留神锁能够降级不可降级,这种策略是为了进步取得锁和开释锁的效率。 锁优化偏差锁偏差锁是Java 6之后退出的新锁,它是一种针对加锁操作的优化伎俩,通过钻研发现,在大多数状况下,锁不仅不存在多线程竞争,而且总是由同一线程屡次取得,因而为了缩小同一线程获取锁(会波及到一些CAS操作,耗时)的代价而引入偏差锁。 偏差锁的核心思想是,如果一个线程取得了锁,那么锁就进入偏差模式,此时Mark Word 的构造也变为偏差锁构造,当这个线程再次申请锁时,无需再做任何同步操作,即获取锁的过程,这样就省去了大量无关锁申请的操作,从而也就提供程序的性能。 所以,对于没有锁竞争的场合,偏差锁有很好的优化成果,毕竟极有可能间断屡次是同一个线程申请雷同的锁。然而对于锁竞争比拟强烈的场合,偏差锁就生效了, 因为这样场合极有可能每次申请锁的线程都是不雷同的,因而这种场合下不应该应用偏差锁,否则会得失相当,须要留神的是,偏差锁失败后,并不会立刻收缩为重量级锁,而是先降级为轻量级锁。上面咱们接着理解轻量级锁。 引入偏差锁次要目标是:为了在无多线程竞争的状况下尽量减少不必要的轻量级锁执行门路。下面提到了轻量级锁的加锁解锁操作是须要依赖屡次CAS原子指令的。 那么偏差锁是如何来缩小不必要的CAS操作呢?咱们能够查看Mark work的构造就明确了。只须要查看是否为偏差锁、锁标识为以及ThreadID即可 获取锁 检测Mark Word是否为可偏差状态,即是否为偏差锁1,锁标识位为01;若为可偏差状态,则测试线程ID是否为以后线程ID,如果是,则执行步骤(5),否则执行步骤(3);如果线程ID不为以后线程ID,则通过CAS操作竞争锁,竞争胜利,则将Mark Word的线程ID替换为以后线程ID,否则执行线程(4);通过CAS竞争锁失败,证实以后存在多线程竞争状况,当达到全局平安点,取得偏差锁的线程被挂起,偏差锁降级为轻量级锁,而后被阻塞在平安点的线程持续往下执行同步代码块;执行同步代码块开释锁偏差锁的开释采纳了一种只有竞争才会开释锁的机制,线程是不会被动去开释偏差锁,须要期待其余线程来竞争。偏差锁的撤销须要期待全局平安点(这个工夫点是上没有正在执行的代码)。其步骤如下: 暂停领有偏差锁的线程,判断锁对象石是否还处于被锁定状态;撤销偏差苏,复原到无锁状态(01)或者轻量级锁的状态;轻量级锁假使偏差锁失败,虚拟机并不会立刻降级为重量级锁,它还会尝试应用一种称为轻量级锁的优化伎俩(1.6之后退出的),此时Mark Word 的构造也变为轻量级锁的构造。 轻量级锁可能晋升程序性能的根据是“对绝大部分的锁,在整个同步周期内都不存在竞争”,留神这是教训数据。须要理解的是,轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间拜访同一锁的场合,就会导致轻量级锁收缩为重量级锁。 引入轻量级锁的次要目标是在多没有多线程竞争的前提下,缩小传统的重量级锁应用操作系统互斥量产生的性能耗费。当敞开偏差锁性能或者多个线程竞争偏差锁导致偏差锁降级为轻量级锁,则会尝试获取轻量级锁。 获取锁判断以后对象是否处于无锁状态(hashcode、0、01),若是,则JVM首先将在以后线程的栈帧中建设一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝(官网把这份拷贝加了一个Displaced前缀,即Displaced Mark Word);否则执行步骤(3);JVM利用CAS操作尝试将对象的Mark Word更新为指向Lock Record的斧正,如果胜利示意竞争到锁,则将锁标记位变成00(示意此对象处于轻量级锁状态),执行同步操作;如果失败则执行步骤(3);判断以后对象的Mark Word是否指向以后线程的栈帧,如果是则示意以后线程曾经持有以后对象的锁,则间接执行同步代码块;否则只能阐明该锁对象曾经被其余线程抢占了,这时轻量级锁须要收缩为重量级锁,锁标记位变成10,前面期待的线程将会进入阻塞状态;开释锁轻量级锁的开释也是通过CAS操作来进行的,次要步骤如下:取出在获取轻量级锁保留在Displaced Mark Word中的数据;用CAS操作将取出的数据替换以后对象的Mark Word中,如果胜利,则阐明开释锁胜利,否则执行(3);如果CAS操作替换失败,阐明有其余线程尝试获取该锁,则须要在开释锁的同时须要唤醒被挂起的线程。对于轻量级锁,其性能晋升的根据是“对于绝大部分的锁,在整个生命周期内都是不会存在竞争的”,如果突破这个根据则除了互斥的开销外,还有额定的CAS操作,因而在有多线程竞争的状况下,轻量级锁比重量级锁更慢; 自旋锁轻量级锁失败后,虚拟机为了防止线程实在地在操作系统层面挂起,还会进行一项称为自旋锁的优化伎俩。 这是基于在大多数状况下,线程持有锁的工夫都不会太长,如果间接挂起操作系统层面的线程可能会得失相当,毕竟操作系统实现线程之间的切换时须要从用户态转换到外围态,这个状态之间的转换须要绝对比拟长的工夫,工夫老本绝对较高,因而自旋锁会假如在不久未来, 以后的线程能够取得锁,因而虚构机会让以后想要获取锁的线程做几个空循环(这也是称为自旋的起因),个别不会太久,可能是50个循环或100循环,在通过若干次循环后,如果失去锁,就顺利进入临界区。 如果还不能取得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化形式,这种形式的确也是能够晋升效率的。最初没方法也就只能降级为重量级锁了。 线程的阻塞和唤醒须要CPU从用户态转为外围态,频繁的阻塞和唤醒对CPU来说是一件累赘很重的工作,势必会给零碎的并发性能带来很大的压力。 同时咱们发现在许多利用下面,对象锁的锁状态只会继续很短一段时间,为了这一段很短的工夫频繁地阻塞和唤醒线程是十分不值得的。所以引入自旋锁。 何谓自旋锁?所谓自旋锁,就是让该线程期待一段时间,不会被立刻挂起,看持有锁的线程是否会很快开释锁。怎么期待呢?执行一段无意义的循环即可(自旋),和CAS相似。 自旋期待不能代替阻塞,先不说对处理器数量的要求(多核,貌似当初没有单核的处理器了),尽管它能够防止线程切换带来的开销,然而它占用了处理器的工夫。 如果持有锁的线程很快就开释了锁,那么自旋的效率就十分好,反之,自旋的线程就会白白消耗掉解决的资源,它不会做任何有意义的工作,典型的占着茅坑不拉屎,这样反而会带来性能上的节约。 所以说,自旋期待的工夫(自旋的次数)必须要有一个限度,如果自旋超过了定义的工夫依然没有获取到锁,则应该被挂起。 自旋锁在JDK 1.4.2中引入,默认敞开,然而能够应用-XX:+UseSpinning开开启,在JDK1.6中默认开启。同时自旋的默认次数为10次,能够通过参数-XX:PreBlockSpin来调整; 如果通过参数-XX:preBlockSpin来调整自旋锁的自旋次数,会带来诸多不便。如果我将参数调整为10,然而零碎很多线程都是等你刚刚退出的时候就开释了锁(如果你多自旋一两次就能够获取锁),你是不是很难堪。 于是JDK1.6引入自适应的自旋锁,让虚构机会变得越来越聪慧。 适应自旋锁JDK 1.6引入了更加聪慧的自旋锁,即自适应自旋锁。所谓自适应就意味着自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋工夫及锁的拥有者的状态来决定。 它怎么做呢?线程如果自旋胜利了,那么下次自旋的次数会更加多,因为虚拟机认为既然上次胜利了,那么此次自旋也很有可能会再次胜利,那么它就会容许自旋期待继续的次数更多。 反之,如果对于某个锁,很少有自旋可能胜利的,那么在当前要或者这个锁的时候自旋的次数会缩小甚至省略掉自旋过程,免得节约处理器资源。 有了自适应自旋锁,随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的情况预测会越来越精确,虚构机会变得越来越聪慧。 锁打消打消锁是虚拟机另外一种锁的优化,这种优化更彻底,Java虚拟机在JIT编译时(能够简略了解为当某段代码行将第一次被执行时进行编译,又称即时编译). 通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种形式打消没有必要的锁,能够节俭毫无意义的申请锁工夫,如下StringBuffer的append是一个同步办法,然而在add办法中的StringBuffer属于一个局部变量,并且不会被其余线程所应用 因而StringBuffer不可能存在共享资源竞争的情景,JVM会主动将其锁打消。 为了保证数据的完整性,咱们在进行操作时须要对这部分操作进行同步控制,然而在有些状况下,JVM检测到不可能存在共享数据竞争,这是JVM会对这些同步锁进行锁打消。 锁打消的根据是逃逸剖析的数据反对。如果不存在竞争,为什么还须要加锁呢?所以锁打消能够节俭毫无意义的申请锁的工夫。 变量是否逃逸,对于虚拟机来说须要应用数据流剖析来确定,然而对于咱们程序员来说这还不分明么?咱们会在明明晓得不存在数据竞争的代码块前加上同步吗? 然而有时候程序并不是咱们所想的那样?咱们尽管没有显示应用锁,然而咱们在应用一些JDK的内置API时,如StringBuffer、Vector、HashTable等,这个时候会存在隐形的加锁操作。 比方StringBuffer的append()办法,Vector的add()办法: COPYpublic void vectorTest(){ Vector<String> vector = new Vector<String>(); for(int i = 0 ; i < 10 ; i++){ vector.add(i + ""); } System.out.println(vector); }在运行这段代码时,JVM能够显著检测到变量vector没有逃逸出办法vectorTest()之外,所以JVM能够大胆地将vector外部的加锁操作打消。 ...

March 1, 2023 · 1 min · jiezi

关于java:Java-后期绑定

绑定绑定指的是一个办法调用与办法所在类(办法主体)关联起来 后期 & 动态 & 编译时绑定若在程序执行前进行绑定(如果有的话,由编译器和连贯程序实现),叫做后期/动态/编译时绑定后期绑定是面向过程语言中默认的绑定形式。这么做意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的相对地址 为何能将调用解析到将要被执行的代码的相对地址?因为后期绑定在程序运行前的编译期间就已将办法调用与办法主体关联起来了然而在OOP(Oriented-Object Programming)中,程序直到运行时才可能确定代码的地址,所以当音讯发送到一个泛化对象时,必须采纳其余机制 办法调用机制,即在运行时判断对象的具体类型,从而调用失当的办法,找到正确的办法执行主体,前期绑定机制随编程语言的不同而有所不同,但无论如何都必须在对象中安置某种【类型信息】前期 & 动静 & 运行时绑定面向对象程序设计语言应用了前期/动静/运行时绑定的概念当向对象发送音讯时,被调用的办法的代码直到运行时能力确定,编译器确保被调用办法的存在,并对被调用参数和返回值执行类型查看(无奈提供此类保障的语言被称为是弱类型的),然而并不知道将要被执行的确切代码 为了执行前期绑定,Java应用一小段非凡的代码来代替相对地址调用,这段代码应用在对象中存储的信息来计算方法体的地址。这样,依据这一小段代码的内容,每一个对象都能够具备不同的行为表现。当向一个对象发送音讯时,该对象就可能晓得这条音讯应该做些什么 前期绑定在运行时依据具体对象类型进行绑定Java中static办法、final办法(private办法属于final办法)属于后期绑定,子类无奈重写final办法,成员变量(包含动态/非动态)也属于后期绑定,除了static和final之外的其余办法属于前期绑定 有了前期绑定,当向一个对象发送音讯时,即便波及向上转型,该对象也只晓得要执行什么样的正确行为 向上转型一个面向对象程序必定会在某处蕴含向上转型upcasting,灵感来源于模型铸造的塑模动作,而向上up这个词来源于继承图的典型布局形式:通常基类在顶部,而导出类在其下部散开,因而,转型为一个基类就是在继承图中向上挪动,即【向上转型】,因为这正是将本人从必须晓得确切类型的限定中解放出来的要害

March 1, 2023 · 1 min · jiezi

关于java:MyBatisPlus-动态数据源

MyBatis-Plus 动静数据源须要引入jar包 <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.3.1</version></dependency>应用@DS()注解,括号内的值是咱们配置的数据源名称,通常应用的时候是配置到mapper层的类上;如果没有该注解则应用的是默认数据库;@DS 的优先级也是就近准则,如果类上已有@DS且他的某个办法也有@DS那么该办法应用的数据库为办法上申明的数据库;有些时候不同库中有雷同的表(对应一个实体类),须要在运行时动静抉择数据源。咱们能够在应用@DS注解的时候,应用#通配符,将咱们的数据库以参数的形式来启用;#参数名称能够动静的应用咱们的参数变量或者header和session中的变量; ‘#’参数的实现依赖于com.baomidou.dynamic.datasource.processorDsProcessor接口解决,实现形式有三个 DsHeaderProcessor: 从申请的header中获取ds数据源名称。DsSessionProcessor: 从session中获取数据源d名称DsSpelExpressionProcessor: 通过spel表达式获取ds数据源名称,这3种形式采纳的是责任链形式间断获取的。顺次执行。 DsHeaderProcessor的拦挡格局为@DS("#header 变量名")@DS("#header dataName")List<Entity> List(@Param("query") String queryDsSessionProcessor的拦挡格局为@DS("#session 变量名")@DS("#session dataName")List<Entity> List(@Param("query") String query@DS("#dataName")List<Entity> List(@Param("query") String query, String dataName);其实现原理为DynamicDataSourceAutoConfiguration配置类Advisor @Bean @ConditionalOnMissingBean public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) { DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor(); interceptor.setDsProcessor(dsProcessor); DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor); advisor.setOrder(properties.getOrder()); return advisor; }DynamicDataSourceAnnotationAdvisor的切点为DS注解 private Pointcut buildPointcut() { Pointcut cpc = new AnnotationMatchingPointcut(DS.class, true); Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(DS.class); return new ComposablePointcut(cpc).union(mpc); }拦截器为DynamicDataSourceAnnotationInterceptor,拦截器会解决以#结尾的注解值,并交由DsProcessor来解决 ...

March 1, 2023 · 2 min · jiezi

关于java:HashMap源码解析

我的所有原创Android常识体系,已打包整顿到GitHub.致力打造一系列适宜初中高级工程师可能看得懂的优质文章,欢送star~ 1. 存储构造1.1 JDK 1.7外部是以数组的模式存储了Entry对象,而每个Entry对象外面有key和value用来存值.它外面蕴含了key、value、next、hash四个字段,其中next字段是用来援用下一个Entry的(雷同的hash值会被放入同一个链表中).数组中的每个地位都是一条单链表(也能够称之为桶),数组中的元素的表头.解决抵触的形式是拉链法,同一条单链表中的Entry的hash值是雷同的. transient Entry<K,V>[] table;static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; int hash;}1.2 JDK 1.8存储构造也是数组,只不过将Entry换了个名字叫Node.1.7中hash值雷同的是放单链表中,1.8也是,当这个单链表的长度超过8时,会转换成红黑树,减少查找效率. 2. 基本概念2.1 负载因子和阈值/** * The default initial capacity - MUST be a power of two. */static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16/** * The load factor used when none specified in constructor. */static final float DEFAULT_LOAD_FACTOR = 0.75f;HashMap的默认大小是16,默认负载因子是0.75,意思是当存的元素个数超过16*0.75时就要对数组扩容,这里的阈值就是16*0.75=12.负载因子就是用来管制什么时候进行扩容的. 阈值 = 以后数组长度*负载因子 ...

March 1, 2023 · 8 min · jiezi

关于java:程序员适合创业吗

大家好,我是良许。 从去年 12 月开始,我曾经在视频号、抖音等支流视频平台上间断更新视频到当初,并失去了不错的评估。 每个视频都花了很多工夫精力用心制作,欢送大家关注哦~ 思考到有些小伙伴没有看过我的视频,在此我将视频脚本分享进去,心愿可能给小伙伴们提供一些帮忙。 程序员适宜守业吗?必须适宜,我就是程序员,当初正在守业,支出是下班时的5倍不止! 那么程序员守业能够做哪些项目呢?我给大家列一列常见的适宜程序员创业项目,最初一个最常见但不倡议。 首先是自媒体相干的,比方公众号,抖音,B站,等等,通过一直分享获取粉丝,而后再进行变现; 第二,社群相干,比方付费社群、常识星球之类,在社群里进行价值输入; 第三,做一些付费课程,比方开专栏、做视频教程,通过卖课变现; 第四,能够做产品,比方小程序、浏览器插件等等; 第五,做培训,每年都有很多人想入行程序员,这方面的蛋糕十分之大! 最初,很多程序员做外包,但我并不倡议,因为做外包无奈做到睡后支出。 还有什么不明确的,欢送评论区交换哦。 互联网大厂勾销996真的反内卷了吗?最近,已经视996为福报的互联网大厂们,带头扛起反996的大旗,这真的是在反内卷吗? 其实不见得,这种扭转不仅没有给员工减轻负担,反而加剧了内卷。 一方面,最直观的就是薪资的缩小。大厂员工之所以可能拿高薪,很大一部分靠的就是加班费,这是业内公开的机密。现在加班缩小了,很多员工支出骤降,甚至都还不起房贷,薪资倒挂的景象更加突出; 另一方面,工作强度岂但没缩小,反而减少了。大厂工作沉重,该干的活还是得干,不在公司加班,就得回家去做。所谓的不加班,只是不在公司加班,一场做秀而已。 然而,从久远来看,这是一个良好的开始,大厂也会缓缓适应新的工作节奏,同时小企业也不会自觉跟风996,对于打工人来讲,必定是利大于弊。 评论区通知我,你们公司还是996吗? 程序员如何从技术转为治理?程序员千万不能只埋头写代码,否则35岁之后,公司优化名单里你必定少不了。 以下5点教你如何从技术转为治理,记得点赞加珍藏哦。 第一,做好本职工作,如果连本职工作都没做好,转治理就是空谈; 第二,解决好与共事之间的关系,治理岗位人际关系可是重点哟; 第三,学会全方面的对待问题,晋升本人思考问题的高度,不能像做技术那样只思考单方面问题; 第四,要开始学习治理常识,思考如何率领团队; 第五,逐渐造就本身的治理能力,一方面是自我晋升,另一方面也让领导看到你的动向; 如果下面的五步你都做好了,那么你接下来要做的就是急躁期待机会。 计算机专业有哪些肯定要加入的较量?计算机专业的同学,想要进大厂,肯定要多加入一些较量。不仅能进步本人的技术水平,还能意识一些业内顶尖高手。 以下5个较量,含金量高,拿下就是高薪。最初一个,要是拿到了,你会成为同学眼中的大神! 第一,蓝桥杯,一年一次,相比拟ACM而言更加亲民的一个抉择;目前是很多计算机专业大学生必加入的较量之一。 第二,LeetCode 周赛 / 双周赛,去 BAT 或者 TMD 面试的小伙伴有可能遇到力扣中的原题。 第三,阿里天池比赛,较量奖金丰富,难度也较大,非计算机专业也能够一试。 第四,Hackathon,俗称「黑客马拉松」,两天的工夫内,通宵实现一个作品并现场演示,对身体素质考验很大哟。 最初一个,ACM,被称为程序员的奥林匹克,含金量最高,难度最大,要是拿到了,相当于一条腿踏进了大厂。 评论区通知我,你都加入了哪一些较量呢? 35岁程序员前途在哪?别再置信「35岁程序员只能送外卖」这样的鬼话了,35岁程序员的前途远比你设想得多! 如果你的技术好,能够持续走技术路线,当架构师或技术专家,率领团队做我的项目,这样的人才,大厂十分喜爱! 如果你治理能力不错,能够走治理路线,先升主管,再升经理,始终升到CTO,成为公司高管,前途无量! 如果你不想写代码了,能够转产品经理,或者项目经理,率领团队一起打造一款牛逼的产品,也是十分不错滴! 如果你手里有资源有人脉,那么能够思考守业,比方做培训、征询、常识付费等等,设想空间就更大了。 如果你曾经在公司里赚了一大笔钱了,那么……你能够退休了 程序员要不要进外包公司?外包公司工资低,加班多,还被区别对待,然而呢,这并不代表不能外包公司。 如果是做驻大厂的外包开发,那这样的机会比很多一般公司要好上100倍!尽管是二等公民,但能够学到大公司的流程标准、代码标准,而且能够接触到很多牛人,集体技术能力能够失去很大的进步。运气好的话,能够间接外包转正。再不济的话,有这样的背景也能够跳到一个十分不错的公司。 如果你是转行的,或者没有更好的offer,那么能够把外包公司当作本人的跳板。因为外包公司门槛较低,你能够疾速积攒很多开发教训,相熟支流我的项目的开发流程,实现富丽的变身。 然而,话又说回来,不倡议在外包公司久呆,当积攒了肯定的技术之后,肯定要换到一家更好的公司持续深造。 程序员最重要的能力是什么?写代码并不是程序员最重要的能力,以下3种能力,把握了,你就离高级工程师不远了,倡议点赞加珍藏哦~ 第一,逻辑思维。 编程语言只是工具而已,最重要的是如何应用编程语言将你的业务转化为代码。逻辑思维强的人,很快就能够实现编码。所以,逻辑思维能力是掂量一个程序员程度的重要指标。 第二,解决问题能力。 程序员至多60%的精力都在解决问题上,而解决问题80%的工夫都是在解决逻辑和Bug当中。所以一个好的程序员解决问题的能力相对是十分强的; 第三,学习能力。 计算机技术更新换代十分快,新技术、新常识、新框架层出不穷,如果你没有足够强的学习能力,就无奈跟上变动,只能吃老本,被淘汰就是迟早的事。 评论区通知我,你都把握了哪些能力? 学习编程,千万不要急于求成,肯定要多读一些经典书籍,多看源码,多下苦功夫去死磕代码,这样技术能力出息。给大家分享一些程序员必读经典书籍,肯定要多读几遍: 收费送给大家,只求大家金指给我点个赞! ...

February 28, 2023 · 1 min · jiezi

关于java:面试半年总结了1000道2023年Java架构师岗面试题

半年前还在迷茫该学什么,怎样才能走出当初的窘境,半年后曾经胜利上岸阿里,感激在这期间帮忙我的每一个人。 面试中总结了1000道经典的Java面试题,外面蕴含面试要答复的常识重点,并且我依据常识类型进行了分类,能够说十分全面了~ 因为文章篇幅问题,以下只展现局部题目内容,须要残缺文档的敌人,点赞之后【点击此处】即可收费获取!根底篇1、 Java语言有哪些特点1、简略易学、有丰盛的类库2、面向对象(Java最重要的个性,让程序耦合度更低,内聚性更高)3、与平台无关性(JVM是Java跨平台应用的基本)4、牢靠平安5、反对多线程 2、面向对象和面向过程的区别面向过程:是剖析解决问题的步骤,而后用函数把这些步骤一步一步地实现,而后在应用的时候一一调用则可。性能较高,所以单片机、嵌入式开发等个别采纳面向过程开发 面向对象:是把形成问题的事务分解成各个对象,而建设对象的目标也不是为了实现一个个步骤,而是为了形容某个事物在解决整个问题的过程中所产生的行为。面向对象有封装、继承、多态的个性,所以易保护、易复用、易扩大。能够设计出低耦合的零碎。 然而性能上来说,比面向过程要低。 JVM篇说说堆和栈的区别栈是运行时单位,代表着逻辑,内含根本数据类型和堆中对象援用,所在区域间断,没有碎片;堆是存储单位,代表着数据,可被多个栈共享(包含成员中根本数据类型、援用和援用对象),所在区域不间断,会有碎片。 1、性能不同栈内存用来存储局部变量和办法调用,而堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。 2、共享性不同栈内存是线程公有的。 堆内存是所有线程共有的。 3、异样谬误不同如果栈内存或者堆内存不足都会抛出异样。 栈空间有余:java.lang.StackOverFlowError。 堆空间有余:java.lang.OutOfMemoryError。 4、空间大小栈的空间大小远远小于堆的 Spring篇1、什么是spring?Spring 是个java企业级利用的开源开发框架。Spring次要用来开发Java利用,然而有些扩大是针对构建J2EE平台的web利用。Spring 框架指标是简化Java企业级利用开发,并通过POJO为根底的编程模型促成良好的编程习惯。 2、你们我的项目中为什么应用Spring框架?这么问的话,就间接说Spring框架的益处就能够了。比如说Spring有以下特点: 轻量:Spring 是轻量的,根本的版本大概2MB。 管制反转:Spring通过管制反转实现了涣散耦合,对象们给出它们的依赖,而不是创立或查找依赖的对象们。 面向切面的编程(AOP):Spring反对面向切面的编程,并且把利用业务逻辑和零碎服务离开。 容器:Spring 蕴含并治理利用中对象的生命周期和配置。 MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。 事务管理:Spring 提供一个继续的事务管理接口,能够扩大到上至本地事务下至全局事务(JTA) 异样解决:Spring 提供方便的API把具体技术相干的异样(比方由JDBC,Hibernate or JDO抛出的)转化为统一的unchecked 异样。 MyBatis篇 SpringBoot篇 MySQL篇说说InnoDB与MyISAM的区别InnoDB反对事务,MyISAM不反对,对于InnoDB每一条SQL语言都默认封装成事务,主动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;InnoDB反对外键,而MyISAM不反对。对一个蕴含外键的InnoDB表转为MYISAM会失败;InnoDB是汇集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。然而辅助索引须要两次查问,先查问到主键,而后再通过主键查问到数据。因而,主键不应该过大,因为主键太大,其余索引也都会很大。而MyISAM是非汇集索引,数据文件是拆散的,索引保留的是数据文件的指针。主键索引和辅助索引是独立的。InnoDB不保留表的具体行数,执行select count(*) from table时须要全表扫描。而MyISAM用一个变量保留了整个表的行数,执行上述语句时只须要读出该变量即可,速度很快;Innodb不反对全文索引,而MyISAM反对全文索引,查问效率上MyISAM要高; SpringCloud篇 Redis篇为什么 Redis 单线程模型效率也能那么高?C语言实现,效率高纯内存操作基于非阻塞的IO复用模型机制单线程的话就能防止多线程的频繁上下文切换问题丰盛的数据结构(全称采纳hash构造,读取速度十分快,对数据存储进行了一些优化,比方亚索表,跳表等) 局部内容展现

February 28, 2023 · 1 min · jiezi

关于java:开源分布式任务调度系统就选DolphinScheduler

分布式任务调度这个话题是每个后端开发和大数据开发都会接触的话题。因为利用场景的宽泛,所以有很多开源我的项目专一于解决这类问题,比方咱们熟知的xxl-job。 那么明天要给大家举荐的则是另一个更为弱小的开源我的项目:DolphinScheduler 介绍 DolphinScheduler是一款开源的分布式任务调度零碎,它能够帮忙开发人员更加不便地进行任务调度和治理。DolphinScheduler反对常见的工作类型,包含Shell、Hadoop、Spark、Hive等,同时它也提供了可视化的工作编排和监控,使得工作治理变得更加简略。DolphinScheduler的开发团队源于支付宝的数据平台团队,经验了多年的实际和积攒,曾经成为了一个成熟的开源我的项目。 特点分布式任务调度 DolphinScheduler反对分布式部署,能够扩大到上千台服务器,实现高可用和高并发的任务调度。 多种工作类型 DolphinScheduler反对多种工作类型,包含Shell、Hadoop、Spark、Hive、MR、Python等,在此基础上还能够反对自定义工作类型,不便用户扩大。 可视化工作编排和监控DolphinScheduler提供了可视化的工作编排和监控,能够不便地查看工作依赖关系、工作执行状况等信息,帮忙用户更好地治理工作。 利用场景数据处理DolphinScheduler能够利用于数据处理场景,例如数据采集、数据荡涤、数据分析等。用户能够通过DolphinScheduler轻松地配置和治理这些工作,同时也能够实现工作的并发执行和分布式解决。 业务流程DolphinScheduler能够利用于业务流程场景,例如电商的订单治理、物流治理等。用户能够通过DolphinScheduler将不同的业务流程工作进行编排,实现自动化执行和监控。 系统集成DolphinScheduler能够利用于系统集成场景,例如将多个零碎的数据进行整合和解决。用户能够通过DolphinScheduler配置和治理不同零碎之间的数据同步和交互工作,实现系统集成的自动化。 论断DolphinScheduler是一款成熟的开源分布式任务调度零碎,它反对多种工作类型、可视化工作编排和监控,实用于不同的利用场景。如果您正在寻找一款任务调度零碎,那么无妨考虑一下DolphinScheduler。 官网地址:https://dolphinscheduler.apac...我的项目地址:https://github.com/apache/dol...欢送关注我的公众号:程序猿DD。第一工夫理解前沿行业音讯、分享深度技术干货、获取优质学习资源

February 28, 2023 · 1 min · jiezi

关于java:译Java-web-应用和虚拟线程

介绍Loom我的项目的指标是为JRE带来易于应用、高吞吐、轻量级并发。Loom的一个个性是虚构线程。在本文中,咱们将摸索在Tomcat上部署的简略wen利用上应用虚构贤臣更意味着什么。 高吞吐/轻量级第一个试验是比拟应用Tomcat规范线程池的开销和应用虚构线程的执行器的开销。测试环境在本文最初给出。应用每秒均匀申请数查看了不同响应大小和申请并发性的性能。后果如下图所示。 结果表明,通常,创立新的虚构线程来解决申请的开销小于从线程池获取平台线程的开销。在线程池测试中看到的一个意外后果是,对于较小的响应主体,2个并发用户每秒产生的均匀申请数比单个用户少。考察发现,在传递给Executor的工作和Executor调用工作的run()办法之间产生了额定的提早。这一差别在4个并发用户中缩小,在8个并发用户简直隐没。 当并发工作多于可用的处理器核时,在高并发级别,虚构线程执行器再次显示出更高的性能。这在应用较小响应体的测试中更为显著。依据图中的测试,(译者注:响应体小于4k时,并发越大越显著。小于16K是会有一定量的进步。再大一些的话和规范线程池差异不大了。 易于应用第二个试验将应用规范线程池的Servlet异步I/O取得的性能与应用基于虚构线程的执行器的简略阻塞I/O取得的成果进行了比拟。虚构线程的潜在益处是简略。与等效的Servlet异步读写相比,阻塞读写要简略得多,特地是在思考错误处理时。 Servlet异步I/O通常用于拜访响应有显著提早的某些内部服务。测试web应用程序在Service类中对此进行了模仿。与基于虚构线程的执行器一起应用的Servlet以阻塞形式拜访服务,而与规范线程池一起应用的Servlet应用Servlet异步API拜访服务。没有波及任何网络IO,但这不应该影响后果。 不出所料,最后的测试显示,阻塞办法和异步办法之间没有可测量的差别,因为计时次要由5秒提早管制。为了在没有提早影响的状况下摸索差别,将提早缩小为零,并执行了一组与吞吐量测试相似的测试。后果如下图所示: 咱们再次看到,虚构线程通常性能更高,在低并发和并发超过可用于测试的处理器内核数量时,差别最为显著。 剖析基于虚构线程的执行器和Tomcat的规范线程池之间的差别并不像从上图中首次看到的那样显著。这些测试旨在查看与每种办法相干的开销,并不代表理论应用程序。在理论利用中,与实现申请所需的工夫相比,测试中显示的差别可能微不足道。 第一个因素:Tomcat的规范线程池和基于虚构线程的执行器之间性能差别的次要因素是线程池队列增加和删除工作的竞争。通过优化Tomcat应用的以后实现,能够缩小规范线程池队列中的锁竞争,并进步吞吐量。 第二个因素:上下文切换。这可能是第二个试验中呈现的性能差别的一种解释,因为虚构线程的上下文切换比规范线程池中的线程的老本更低,因而并发性超过了可用的处理器内核数。 论断应用基于虚构线程的执行器是Tomcat规范线程池的可行代替计划。就容器开销而言,切换到虚构线程执行器的益处微不足道。 经验阻塞诸的Web应用程序(比方Tomcat上经典的Spring MVC利用),并且尚未切换到Servlet异步API,响应式编程或其余异步API,应该通过切换到基于虚构线程的执行器来看到一些可扩展性的改良。依据Web应用程序,能够实现这些改良,而不会更改Web利用程序代码。切换到应用Servlet异步API、响应式编程或其余异步API的Web应用程序不太可能通过切换到基于虚构线程的执行器来察看到可测量的差别(侧面或负面),除非单机需承担的QPS特地大。 从久远来看,虚构线程的最大益处仿佛是更简略的利用程序代码。以后须要应用Servlet异步API、反应式编程或其余异步API的一些用例将可能应用阻塞IO和虚构线程来满足。对此须要留神的是,应用程序通常须要对不同的内部服务进行屡次调用。这是虚构线程最无效的并行实现,尽管Project Reactor等框架为此提供了一流的反对,但JRE的同类解决方案(结构化并发)仍处于孵化器阶段,仅旨在协调多个Future,而不是以最不便的形式互相申明或组合它们。 最初,Loom我的项目仍处于预览模式。当初思考在生产环境中应用虚构线程还为时过早,但当初是将Loom我的项目和虚构线程纳入布局的时候了,这样当JRE中正式有虚构线程时,您就能够做好筹备了。 测试环境未译,原文地址: https://spring.io/blog/2023/0...

February 28, 2023 · 1 min · jiezi

关于java:11-款顶级-MySQL-图形化工具汇总总有一款适合你建议收藏

MySQL 是一个十分风行的小型关系型数据库管理系统,2008年1月16号被Sun公司收买。目前 MySQL 被宽泛地利用在中小型 网站中。因为其体积小、速度快、总体领有成本低,尤其是开放源码这一特点,许多中小型网站为了升高网站总体领有老本而抉择了 MySQL 作为网站数据库。 MySQL 的治理保护工具十分多,除了零碎自带的命令行管理工具之外,还有许多其余的图形化管理工具,工具好用是一方面,集体的应用习惯也很重要,这里介绍 11 款 MySQL 图形化管理工具,供大家参考。 Spring Boot 根底就不介绍了,举荐看这个收费教程: https://github.com/javastacks/spring-boot-best-practice1、DBeaver DBeaver 是一个基于 Java 开发,收费开源的通用数据库治理和开发工具,应用十分敌对的 ASL 协定。能够运行在各种操作系统上,包含:Windows、Linux、macOS 等。DBeaver 采纳 Eclipse 框架开发,反对插件扩大,并且提供了许多数据库管理工具:ER 图、数据导入/导出、数据库比拟、模仿数据生成等。能够反对简直所有的数据库产品,包含:MySQL、PostgreSQL、MariaDB、SQLite、Oracle、Db2、SQL Server、Sybase、MS Access、Teradata、Firebird、Derby 等等。 官网地址:https://dbeaver.io/2、DataGrip DataGrip 是 JetBrains 公布的多引擎数据库环境,反对 MySQL 和 PostgreSQL,Microsoft SQL Server 和 Oracle,Sybase,DB2,SQLite,还有 HyperSQL,Apache Derby 和 H2。 官网地址:https://www.jetbrains.com/zh-...3、phpMyAdmin phpMyAdmin是最罕用的MySQL保护工具,是一个用PHP开发的基于Web形式架构在网站主机上的MySQL管理工具,反对中文,治理数据库十分不便。不足之处在于对大数据库的备份和复原不不便。 官网地址:https://www.phpmyadmin.net/4、MySQLDumper MySQLDumper应用PHP开发的MySQL数据库备份恢复程序,解决了应用PHP进行大数据库备份和复原的问题,数百兆的数据库都能够不便的备份复原,不必放心网速太慢导致两头中断的问题,十分不便易用。这个软件是德国人开发的,还没有中文语言包。 官网地址:https://sourceforge.net/proje...5、Navicat Navicat是一个桌面版MySQL数据库治理和开发工具。和微软SQLServer的管理器很像,易学易用。Navicat应用图形化的用户界面,能够让用户应用和治理更为轻松。反对中文,有收费版本提供。 官网地址:https://www.navicat.com.cn/6、MySQL GUI Tools MySQL GUI Tools是MySQL官网提供的图形化管理工具,性能很弱小,值得举荐,惋惜的是没有中文界面。 官网地址:https://downloads.mysql.com/a...7、MySQL ODBC Connector MySQL官网提供的ODBC接口程序,零碎装置了这个程序之后,就能够通过ODBC来拜访MySQL,这样就能够实现SQLServer、Access和MySQL之间的数据转换,还能够反对ASP拜访MySQL数据库。 官网地址:https://downloads.mysql.com/a...8、MySQL Workbench MySQL Workbench是一个对立的可视化开发和治理平台,该平台提供了许多高级工具,可反对数据库建模和设计、查问开发和测试、服务器配置和监督、用户和平安治理、备份和复原自动化、审计数据查看以及向导驱动的数据库迁徙。MySQL Workbench是MySQL AB公布的可视化的数据库设计软件,它的前身是 FabForce 公司的 DDesigner 4。MySQL Workbench 为数据库管理员、程序开发者和零碎规划师提供可视化设计、模型建设、以及数据库治理性能。它蕴含了用于创立简单的数据建模ER模型,正向和逆向数据库工程,也能够用于执行通常须要破费大量工夫和须要的难以变更和治理的文档工作。MySQL工作台可在Windows,Linux和Mac上应用。 ...

February 28, 2023 · 1 min · jiezi

关于java:Synchronized我要一层一层剥开你的心

三种利用形式润饰实例办法,作用于以后实例加锁,进入同步代码前要取得以后实例的锁。润饰静态方法,作用于以后类对象加锁,进入同步代码前要取得以后类对象的锁。润饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要取得给定对象。润饰实例办法所谓的实例对象锁就是用synchronized润饰实例对象中的实例办法,留神是实例办法不包含静态方法,如下COPYpublic class AccountingSync implements Runnable{ //共享资源(临界资源) static int i=0; /** * synchronized 润饰实例办法 */ public synchronized void increase(){ i++; } @Override public void run() { for(int j=0;j<1000000;j++){ increase(); } } public static void main(String[] args) throws InterruptedException { AccountingSync instance=new AccountingSync(); Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } /** * 输入后果: * 2000000 */}上述代码中,咱们开启两个线程操作同一个共享资源即变量i,因为i++;操作并不具备原子性,该操作是先读取值,而后写回一个新值,相当于原来的值加上1,分两步实现,如果第二个线程在第一个线程读取旧值和写回新值期间读取i的域值,那么第二个线程就会与第一个线程一起看到同一个值,并执行雷同值的加1操作,这也就造成了线程平安失败,因而对于increase办法必须应用synchronized润饰,以便保障线程平安。此时咱们应该留神到synchronized润饰的是实例办法increase,在这样的状况下,以后线程的锁便是实例对象instance,留神Java中的线程同步锁能够是任意对象。从代码执行后果来看的确是正确的,假使咱们没有应用synchronized关键字,其最终输入后果就很可能小于2000000,这便是synchronized关键字的作用。这里咱们还须要意识到,当一个线程正在拜访一个对象的 synchronized 实例办法,那么其余线程不能拜访该对象的其余 synchronized 办法,毕竟一个对象只有一把锁,当一个线程获取了该对象的锁之后,其余线程无奈获取该对象的锁,所以无法访问该对象的其余synchronized实例办法,然而其余线程还是能够拜访该实例对象的其余非synchronized办法,当然如果是一个线程 A 须要拜访实例对象 obj1 的 synchronized 办法 f1(以后对象锁是obj1),另一个线程 B 须要拜访实例对象 obj2 的 synchronized 办法 f2(以后对象锁是obj2),这样是容许的,因为两个实例对象锁并不同雷同,此时如果两个线程操作数据并非共享的,线程平安是有保障的,遗憾的是如果两个线程操作的是共享数据,那么线程平安就有可能无奈保障了,如下代码将演示出该景象 ...

February 28, 2023 · 4 min · jiezi

关于java:CopyOnWriteArrayList源码解析

1. 原理CopyOnWriteArrayList有点像线程平安的ArrayList. 其实它的原理简略概括起来就是读写拆散.写操作是在一个复制的数组上进行的,读操作在原始数组中进行,读写是拆散的.写操作的时候是加锁了的,写操作实现了之后将原来的数组指向新的数组. 上面咱们简略看下add和get办法是如何实现写读操作的. /** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); }}@SuppressWarnings("unchecked")private E get(Object[] a, int index) { return (E) a[index];}/** * {@inheritDoc} * * @throws IndexOutOfBoundsException {@inheritDoc} */public E get(int index) { return get(getArray(), index);}2. 实用场景因为每次写数据的时候都会开拓一个新的数组,这样就会消耗内存,而且加锁了,写的性能不是很好.而读操作是十分迅速的,并且还反对在写的同时能够读. ...

February 28, 2023 · 3 min · jiezi

关于java:配置文件密码加密

1、依赖 <!-- 数据库加密配置--> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency>2、生成加密账号密码 public class Encryption { public static void main(String[] args) { String username = "root"; String password = "root"; System.out.println("待加密名称:" + username); System.out.println("待加密明码:" + password); StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); encryptor.setAlgorithm("PBEWithMD5AndDES");//这是加密形式,采纳MD5和DES的对称加密 encryptor.setPassword("root");//这是加盐,salt。减少密文不被撞破的概率 String enc_name = encryptor.encrypt(username); String enc_pwd = encryptor.encrypt(password); System.out.println("加密后名称:" + enc_name); System.out.println("加密后明码:" + enc_pwd); String redisPwd = "Password123@mysql"; String enc_redis = encryptor.encrypt(redisPwd); System.out.println("redis Pwd解密后:" + enc_redis); String dec_name = encryptor.decrypt(enc_name); String dec_pwd = encryptor.decrypt(enc_pwd); System.out.println("解密后名称:" + dec_name); System.out.println("解密后明码:" + dec_pwd); }}3、配置文件加密 ...

February 28, 2023 · 1 min · jiezi

关于java:三天吃透Java并发八股文

本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址:https://github.com/Tyson0314/... 线程池线程池:一个治理线程的池子。 为什么平时都是应用线程池创立线程,间接new一个线程不好吗?嗯,手动创立线程有两个毛病 不受控危险频繁创立开销大为什么不受控? 系统资源无限,每个人针对不同业务都能够手动创立线程,并且创立线程没有统一标准,比方创立的线程有没有名字等。当零碎运行起来,所有线程都在抢占资源,毫无规定,凌乱局面可想而知,不好管控。 频繁手动创立线程为什么开销会大?跟new Object() 有什么差异? 尽管Java中万物皆对象,然而new Thread() 创立一个线程和 new Object()还是有区别的。 new Object()过程如下: JVM调配一块内存 M在内存 M 上初始化该对象将内存 M 的地址赋值给援用变量 obj创立线程的过程如下: JVM为一个线程栈分配内存,该栈为每个线程办法调用保留一个栈帧每一栈帧由一个局部变量数组、返回值、操作数堆栈和常量池组成每个线程取得一个程序计数器,用于记录以后虚拟机正在执行的线程指令地址零碎创立一个与Java线程对应的本机线程将与线程相干的描述符增加到JVM外部数据结构中线程共享堆和办法区域创立一个线程大略须要1M左右的空间(Java8,机器规格2c8G)。可见,频繁手动创立/销毁线程的代价是十分大的。 为什么应用线程池?升高资源耗费。通过反复利用已创立的线程升高线程创立和销毁造成的耗费。进步响应速度。当工作达到时,能够不须要等到线程创立就能立刻执行。进步线程的可管理性。对立治理线程,防止零碎创立大量同类线程而导致耗费完内存。线程池执行原理? 当线程池里存活的线程数小于外围线程数corePoolSize时,这时对于一个新提交的工作,线程池会创立一个线程去解决工作。当线程池外面存活的线程数小于等于外围线程数corePoolSize时,线程池外面的线程会始终存活着,就算闲暇工夫超过了keepAliveTime,线程也不会被销毁,而是始终阻塞在那里始终期待工作队列的工作来执行。当线程池外面存活的线程数曾经等于corePoolSize了,这是对于一个新提交的工作,会被放进工作队列workQueue排队期待执行。当线程池外面存活的线程数曾经等于corePoolSize了,并且工作队列也满了,假如maximumPoolSize>corePoolSize,这时如果再来新的工作,线程池就会持续创立新的线程来解决新的工作,晓得线程数达到maximumPoolSize,就不会再创立了。如果以后的线程数达到了maximumPoolSize,并且工作队列也满了,如果还有新的工作过去,那就间接采纳回绝策略进行解决。默认的回绝策略是抛出一个RejectedExecutionException异样。线程池参数有哪些?ThreadPoolExecutor 的通用构造函数: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);1、corePoolSize:当有新工作时,如果线程池中线程数没有达到线程池的根本大小,则会创立新的线程执行工作,否则将工作放入阻塞队列。当线程池中存活的线程数总是大于 corePoolSize 时,应该思考调大 corePoolSize。 2、maximumPoolSize:当阻塞队列填满时,如果线程池中线程数没有超过最大线程数,则会创立新的线程运行工作。否则依据回绝策略解决新工作。非核心线程相似于长期借来的资源,这些线程在闲暇工夫超过 keepAliveTime 之后,就应该退出,防止资源节约。 3、BlockingQueue:存储期待运行的工作。 4、keepAliveTime:非核心线程闲暇后,放弃存活的工夫,此参数只对非核心线程无效。设置为0,示意多余的闲暇线程会被立刻终止。 5、TimeUnit:工夫单位 TimeUnit.DAYSTimeUnit.HOURSTimeUnit.MINUTESTimeUnit.SECONDSTimeUnit.MILLISECONDSTimeUnit.MICROSECONDSTimeUnit.NANOSECONDS6、ThreadFactory:每当线程池创立一个新的线程时,都是通过线程工厂办法来实现的。在 ThreadFactory 中只定义了一个办法 newThread,每当线程池须要创立新线程就会调用它。 public class MyThreadFactory implements ThreadFactory { private final String poolName; public MyThreadFactory(String poolName) { this.poolName = poolName; } public Thread newThread(Runnable runnable) { return new MyAppThread(runnable, poolName);//将线程池名字传递给构造函数,用于辨别不同线程池的线程 }}7、RejectedExecutionHandler:当队列和线程池都满了的时候,依据回绝策略解决新工作。 ...

February 27, 2023 · 8 min · jiezi

关于java:三行代码让你的git记录保持整洁

前言笔者最近在主导一个我的项目的架构迁徙工作,因为迁徙我的项目的历史包袱较重,人员单干较多,在迁徙过程中免不了进行多分支、屡次commit的状况,工夫一长,git的提交记录便凌乱不堪,轻易截一个图形化的git提交历史给大家感受一下。 各种分支疯狂打架宛如后宫争宠的妃子们,之所以会呈现这种状况,次要还是因为滥用git merge命令并且不思考后续的了解老本导致的。现在在大厂工作的程序员们,频繁承受变更的需要,一旦一开始考虑不周到,就肯定会呈现了大量无意义的commit log,加上“麻利”理念的推广,产品的疾速迭代上线变成了外围指标,这些无意义的commit log便被“下次再解决”,长此以往就凌乱不堪了。而咱们在看一些开源仓库时,会发现他们的commit记录非常整洁,其实这并不是社区的程序员能力更强,而是因为他们没有KPI大棒的鞭策,在提交代码前会花工夫整顿本人的commit log。而这就是本文的配角了——“Git Rebase”。git rebase和git mergegit rebase,中文翻译为“变基”,通常用于分支合并。既然提到了分支合并,那就肯定离不开git merge这个命令。置信每个老手程序员刚进入职场的时候,都会听到“xxx你把这个分支merge一下”这样的话。那么问题来了,如果你有6个程序员一起工作, 你就会有6个程序员的分支, 如果你应用merge, 你的代码历史树就会有六个branch跟这个主的branch交错在一起。 上图是 git merge 操作的流程示意图,Merge命令会保留所有commit的历史工夫。每个人对代码的提交是各式各样的。只管这些工夫对于程序自身并没有任何意义。然而merge的命令初衷就是为了保留这些工夫不被批改。于是也就造成了以merge工夫为基准的网状历史构造。每个分支上都会持续保留各自的代码记录,主分支上只保留merge的历史记录。子分支随时都有可能被删除。子分子删除当前,你可能看到的记录也就是,merge某branch到某branch上了。这个历史记录形容基本上是没有意义的。而 git rebase 中文翻译为“变基”,变得这个基指的是基准。如何了解这个基准呢?咱们看一下下图。 咱们能够看到通过变基后的feature分支的基准分支产生了变动,变成了最新的master。这就是所谓的“变基”。通过下面的两张图能够很显著的发现,这两种合并分支的形式最大的区别在于,merge后的分支,会保留两个分支的操作记录,这在git commit log 树中会以穿插的模式保留。而rebase后的分支会基于最新的master分支,从而不会造成分叉,从头至尾都是一条洁净的直线。 对于 git rebase 和 git merge 的具体用法不在本文的介绍范畴内,详情能够参考互联网上的其余材料。 在变基过程中,咱们通常须要进行commit的批改,而这也为咱们整顿git记录提供了一个可选计划。放弃最近的几条记录整洁假如咱们有一个仓库,我在这个仓库里执行了4次提交,通过 git reflog 命令查看提交记录如下。 如果咱们想将Commit-3、Commit-2和Commit-1的提交合并成一次提交(假如某次提交至改了一些pom文件),咱们能够间接执行上面的命令git rebase -i HEAD~3复制代码-i 指的是 --interactive ,HEAD~3 指的是最近三次commit。当然咱们也能够间接指定最新的一个想保留的 Commit的ID,在下面的例子中就是Commit-0的ID,因而咱们也能够写成git rebase -i d2b9b78复制代码执行该命令后,咱们会进入到这么如下一个界面: 这个界面是一个Vim界面,咱们能够在这个界面中查看、编辑变更记录。无关Vim的操作,能够看我之前写的文章和录制的视频《和Vim的初次见面》在看前三行之前,咱们先来看一下第5行的命令加深一下咱们对git rebase的意识。 翻译过去就是,将d2b9b78..0e65e22这几个分支变基到d2b9b78这个分支,也就是将Commit-3/2/1/0这几次变更合并到Commit-0上。回到后面三行,这三行示意的是咱们须要操作的三个 Commit,每行最后面的是对该 Commit 操作的 Command。而每个命令指的是什么,命令行里都曾经具体的通知咱们了。 pick:应用该commitsquash:应用该 Commit,但会被合并到前一个 Commit 当中fixup:就像 squash 那样,但会摈弃这个 Commit 的 Commit message ...

February 27, 2023 · 1 min · jiezi

关于java:面试官怎么删除-HashMap-中的元素我一行代码搞定赶紧拿去用

背景大家好,我是栈长。 前些天,栈长给大家分享了两篇有意思的文章: 带了一个 3 年的开发,不会循环删除 List 中的元素,我几乎解体!!面试官:怎么去除 List 中的反复元素?我一行代码搞定,连忙拿去用!这两篇文章的确能帮忙一大部分人,其中分享的一些实现技巧,编程很多年的高手也不肯定用过,不论本人程度多牛,还是多虚心好学一些,把握多一点总不是什么好事。 有粉丝倡议栈长出一篇删除 HashMap 外面的数据,也有粉丝倡议出一个系列的文章: 那这篇就分享下如何删除 HashMap 中的元素吧! PS: 这仅是我集体把握的实现计划,不肯定全,也不肯定是最优的,欢送大家分享,杠精勿扰。HashMap 删除元素计划假如有以下数据: public Map<String, String> initMap = new HashMap<>() {{ put("user1", "张三"); put("user2", "李四"); put("user3", "张三"); put("user4", "李四"); put("user5", "王五"); put("user6", "赵六"); put("user7", "李四"); put("user8", "王五");}};本文所有残缺示例源代码曾经上传: https://github.com/javastacks...欢送 Star 学习,前面 Java 示例都会在这下面提供! 个别删除 HashMap 汇合中的元素,如果晓得具体的 Key,并且须要依据 Key 删除元素,应用 remove 办法就能够了。然而如何依据 Value 删除 HashMap 汇合中的元素呢?这才是你必须把握的技巧!1、应用 for 循环删除/** * 应用 for 循环删除 * @author: 栈长 * @from: 公众号Java技术栈 */@Testpublic void remove1() { Set<Map.Entry<String, String>> entries = new CopyOnWriteArraySet<>(initMap.entrySet()); for (Map.Entry<String, String> entry : entries) { if ("张三".equals(entry.getValue())) { initMap.remove(entry.getKey()); } } System.out.println(initMap);}输入后果: ...

February 27, 2023 · 2 min · jiezi

关于java:LinkedList源码解析

一、概述LinkedList,绝对于ArrayList,大家可能平时应用LinkedList要少一些,其实有时候应用LinkedList比ArrayList效率高很多,当然,这得视状况而定。本文将带大家深刻LinkedList源码,剖析其背地的实现原理,以便当前在适合的状况下进行应用。 之前我所晓得的LinkedList的常识: LinkedList底层是链表构造插入和删除比拟快(O(1)),查问则绝对慢一些(O(n))因为是链表构造,所以调配的空间不要求是间断的二、链表因为LinkedList源码中很多中央是进行链表操作,所以先带大家温习一下链表的基础知识.以前用C语言实现的链表,大家能够去看一下,地址:https://github.com/xfhy/dataS...1. 单链表 一个节点中蕴含数据和下一个节点的指针(留神,是下一个节点的指针,而不是下一个节点数据的指针),尾节点没有下一个节点,所以指向null.拜访某个节点只能从头节点开始查找,而后顺次往后遍历. 2. 单向循环链表 单向循环链表比单链表多了一个尾节点的指针指向的是头结点. 3. 双向链表 双向链表的每个节点蕴含以下数据:上一个节点的指针,本人的数据,下一个节点的指针.尾节点没有下一个节点,所以指向null.这样的构造,比方我拿到链表两头的一个节点,即能够往前遍历,也能够往后遍历. 4. 双向循环链表 双向循环链表的尾节点的下一个节点是头结点,头节点的上一个节点是尾节点. 三、LinkedList的继承关系 源码中的定义: public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.SerializableAbstractSequentialList这个类提供了List的一个骨架实现接口,以尽量减少实现此接口所需的工作量由“程序拜访”数据存储(如链接列表)反对。对于随机拜访数据(如数组),应应用AbstractList优先于此类。实现了List接口,意味着LinkedList元素是有序的,能够反复的,能够有null元素的汇合.Deque是Queue的子接口,Queue是一种队列模式,而Deque是双向队列,它反对从两个端点方向检索和插入元素.实现了Cloneable接口,标识着能够它能够被复制.留神,ArrayList外面的clone()复制其实是浅复制(不晓得此概念的赶快去查资料,这知识点十分重要).实现了Serializable 标识着汇合可被序列化。四、看LinkedList源码前的筹备1. 节点定义private static class Node<E> { E item; //该节点的数据 Node<E> next; //指向下一个节点的指针 Node<E> prev; //指向上一个节点的指针 Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; }}Node是LinkedList的动态外部类. 为什么是动态外部类?我感觉可能起因如下:一般外部类会有外部类的强援用,而动态外部类就没有.有外部类的强援用的话,很容易造成内存透露,写成动态外部类能够防止这种状况的产生. 2. 成员变量看构造方法之前先看看几个属性: //链表长度transient int size = 0;/*** 头结点*/transient Node<E> first;/*** 尾节点*/transient Node<E> last;这里为什么要存在一个成员变量尾节点?我感觉是为了不便,比方查找相应索引处元素+插入元素到最初.查找相应索引处元素时,先判断索引是在前半段还是在后半段,如果是在后半段,那么间接从尾节点登程,从后往前进行查找,这样速度更快.在插入元素到最初时,能够间接通过尾节点不便的进行插入. ...

February 27, 2023 · 8 min · jiezi

关于java:一个更适合Java初学者的轻量级开发工具BlueJ

Java是世界上最风行的编程语言之一,它被宽泛用于从Web开发到挪动利用的各种应用程序。大部分Java工程师次要是用IDEA、Eclipse为主,这两个开发工具因为有弱小的能力,所以复杂度上就更高一些。如果您刚刚开始应用Java,或者您更适宜从一个轻量级的开发环境开始。所以,明天就给大家举荐一个比IDEA更好的抉择:BlueJ BlueJ简介BlueJ是一个收费的、开源的Java开发环境,专为初学者设计。它由英国肯特大学开发,已被寰球数百万学生和教育工作者应用。 它最大的长处就是简略!界面洁净直观,用户能够很容易的开始编写你的第一个Java程序。 特点只管它很简略,但BlueJ依然有很多弱小的性能,使它成为初学者和有教训的开发人员的绝佳抉择。其中一些性能包含: 面向对象的设计 BlueJ围绕Java编程中的面向对象设计原理构建,这是一个基本概念。这意味着您能够轻松创建对象、类和办法,并以易于了解和保护的形式组织您的代码。 可视化调试器 调试代码是开发过程的重要局部,BlueJ的可视化调试器使其变得简略。您能够逐行查看代码、设置断点并实时查看变量。 扩大API 如果须要扩大BlueJ的性能,您能够应用其扩大API创立本人的插件和附加组件。这使您能够依据本人的须要定制开发环境。 所以,如果您是Java编程的老手,或者正在寻找一个轻量级的开发环境,易于应用,那么BlueJ是一个绝佳的抉择。它是收费的、开源的,并且领有弱小的性能,使其成为初学者和有教训的开发人员的绝佳工具。尝试一下,看看它如何帮忙您将Java编程技能晋升到更高的程度! 欢送关注我的公众号:程序猿DD。第一工夫理解前沿行业音讯、分享深度技术干货、获取优质学习资源

February 27, 2023 · 1 min · jiezi

关于java:一个诡异的-Pulsar-InterruptedException-异常

背景 明天收到业务团队反馈线上有个利用往 Pulsar 中发送音讯失败了,通过日志查看得悉是发送音讯时候抛出了 java.lang.InterruptedException 异样。 和业务沟通后得悉是在一个 gRPC 接口中触发的音讯发送,大概继续了半个小时的异样后便恢复正常了,这是整个问题的背景。 前置排查拿到该问题后首先排查下是否是共性问题,查看了其余的利用没有发现相似的异样;同时也查看了 Pulsar broker 的监控大盘,在这个时间段仍然没有稳定和异样; 这样能够初步排除是 Pulsar 服务端的问题。 接着便是查看利用那段时间的负载状况,从利用 QPS 到 JVM 的各个内存状况仍然没发现有什么显著的变动。 Pulsar 源码排查既然看起来利用自身和 Pulsar broker 都没有问题的话那就只能从异样自身来排查了。 首先第一步要得悉具体应用的是 Pulsar-client 是版本是多少,因为业务应用的是外部基于官网 SDK 封装 springboot starter 所以第一步还得排查这个 starter 是否有影响。 通过查看源码根本排除了 starter 的嫌疑,外面只是简略的封装了 SDK 的性能而已。 org.apache.pulsar.client.api.PulsarClientException: java.util.concurrent.ExecutionException: org.apache.pulsar.client.api.PulsarClientException: java.lang.InterruptedException at org.apache.pulsar.client.api.PulsarClientException.unwrap(PulsarClientException.java:1027) at org.apache.pulsar.client.impl.TypedMessageBuilderImpl.send(TypedMessageBuilderImpl.java:91) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: java.util.concurrent.ExecutionException: org.apache.pulsar.client.api.PulsarClientException: java.lang.InterruptedException at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395) at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999) at org.apache.pulsar.client.impl.TypedMessageBuilderImpl.send(TypedMessageBuilderImpl.java:89) ... 49 common frames omitted Caused by: org.apache.pulsar.client.api.PulsarClientException: java.lang.InterruptedException at org.apache.pulsar.client.impl.ProducerImpl.canEnqueueRequest(ProducerImpl.java:775) at org.apache.pulsar.client.impl.ProducerImpl.sendAsync$original$BWm7PPlZ(ProducerImpl.java:393) at org.apache.pulsar.client.impl.ProducerImpl.sendAsync$original$BWm7PPlZ$accessor$i7NYMN6i(ProducerImpl.java) at org.apache.pulsar.client.impl.ProducerImpl$auxiliary$EfuVvJLT.call(Unknown Source) at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86) at org.apache.pulsar.client.impl.ProducerImpl.sendAsync(ProducerImpl.java) at org.apache.pulsar.client.impl.ProducerImpl.internalSendAsync(ProducerImpl.java:292) at org.apache.pulsar.client.impl.ProducerImpl.internalSendWithTxnAsync(ProducerImpl.java:363) at org.apache.pulsar.client.impl.PartitionedProducerImpl.internalSendWithTxnAsync(PartitionedProducerImpl.java:191) at org.apache.pulsar.client.impl.PartitionedProducerImpl.internalSendAsync(PartitionedProducerImpl.java:167) at org.apache.pulsar.client.impl.TypedMessageBuilderImpl.sendAsync(TypedMessageBuilderImpl.java:103) at org.apache.pulsar.client.impl.TypedMessageBuilderImpl.send(TypedMessageBuilderImpl.java:82) ... 49 common frames omitted Caused by: java.lang.InterruptedException: nullat java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1343) at java.base/java.util.concurrent.Semaphore.acquire(Semaphore.java:318) at org.apache.pulsar.client.impl.ProducerImpl.canEnqueueRequest(ProducerImpl.java:758)接下来便只能是剖析堆栈了,因为 Pulsar-client 的局部实现源码是没有间接打包到依赖中的,反编译的话许多代码行数对不上,所以须要将官网的源码拉到本地,切换到对于的分支进行查看。 ...

February 27, 2023 · 2 min · jiezi

关于java:Spring-Boot-实现日志链路追踪无需引入组件让日志定位更方便

起源:blog.csdn.net/qq_35387940/article/details/125062368 前言从文章题目就晓得,这篇文章是介绍些什么。 这是我一位敌人的问题反馈: 如同是的,的确这种景象是普遍存在的。 有时候一个业务调用链场景,很长,调了各种各样的办法,看日志的时候,各个接口的日志交叉,的确让人头大。 含糊匹配搜寻日志能解决吗? 能解决一点点。 然而不能齐全呈现出整个链路相干的日志。 那要做到不便,很显然,咱们须要的是把同一次的业务调用链上的日志串起来。 什么成果? 先看一个实现后的效果图: 这样下来,咱们再配合含糊匹配查找日志,成果不就刚刚的了。 cat -n info.log |grep "a415ad50dbf84e99b1b56a31aacd209c"或者 grep -10 'a415ad50dbf84e99b1b56a31aacd209c' info.log (10是指高低10行)不多说,开整。 注释常规,先看一眼这次实战最终工程的构造: Spring Boot 根底就不介绍了,举荐看这个收费教程: https://github.com/javastacks/spring-boot-best-practice①pom.xml 依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> <!--lombok配置--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency></dependencies>②整合logback,打印日志,logback-spring.xml (简略配置下) <?xml version="1.0" encoding="UTF-8"?><configuration debug="false"> <!--日志存储门路--> <property name="log" value="D:/test/log" /> <!-- 控制台输入 --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--输入格式化--> <pattern>[%X{TRACE_ID}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!-- 按天生成日志文件 --> <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件名--> <FileNamePattern>${log}/%d{yyyy-MM-dd}.log</FileNamePattern> <!--保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>[%X{TRACE_ID}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--日志文件最大的大小--> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- 日志输入级别 --> <root level="INFO"> <appender-ref ref="console" /> <appender-ref ref="file" /> </root></configuration>application.yml ...

February 27, 2023 · 3 min · jiezi

关于java:Spring-Boot-单元测试保姆级教程

起源:eamonyin.blog.csdn.net 一、 单元测试的概念概念: 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。在Java中单元测试的最小单元是类。单元测试是开发者编写的一小段代码,用于测验被测代码的一个很小的、很明确的性能是否正确。执行单元测试,就是为了证实这 段代码的行为和咱们冀望是否统一。单元测试援用: 家喻户晓,通过spring initialize创立的Spring Boot我的项目会在Maven中主动携带很多starter依赖: 其中蕴含了一个名为spring-boot-starter-test的依赖,本文是围绕这个依赖开展。 Spring Boot 根底就不介绍了,举荐看这个收费教程: https://github.com/javastacks/spring-boot-best-practiceSpring Boot中引入单元测试很简略,增加如下依赖(即spring-boot-starter-test依赖): <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency>spring-boot-starter-test有如下几个库: spring-boot-starter-testUML图: 二、单元测试的作用在没有接触单元测试之前咱们是怎么做测试的?个别有两个办法: 在工夫容许的状况下,编写单元测试是程序员对代码的自测,这是对本人代码的负责。 写单元测试的两个动机: 保障或验证实现性能。爱护曾经实现的性能不被毁坏。三、Spring Boot引入的MockMvc的概念什么是Mock?在面向对象的程序设计中,模仿对象(英语:mock object)是以可控的形式模仿实在对象行为的假对象。在编程过程中,通常通过模仿一些输出数据,来验证程序是否达到预期后果。 为什么应用Mock对象?应用模仿对象,能够模仿简单的、实在的对象行为。如果在单元测试中无奈应用实在对象,可采纳模仿对象进行代替。 MockMvc的概念MockMvc是由spring-test包提供,实现了对Http申请的模仿,可能间接应用网络的模式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,后果的验证非常不便。 接口MockMvcBuilder,提供一个惟一的build办法,用来结构MockMvc。次要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder。 MockMVC的根本步骤(1) mockMvc.perform执行一个申请。 (2) MockMvcRequestBuilders.get(“XXX”)结构一个申请。 (3) ResultActions.param增加申请传值 (4) ResultActions.accept()设置返回类型 (5) ResultActions.andExpect增加执行实现后的断言。 (6) ResultActions.andDo增加一个后果处理器,示意要对后果做点什么事件,比方处应用print()输入整个响应后果信息。 (7) ResultActions.andReturn示意执行实现后返回相应的后果。 四、Service层的单元测试第一步: Spring Boot中单元测试类写在src/test/java目录下,你能够手动创立具体测试类,也能够通过IDEA主动创立测试类,如下图:(注:点选并关上相应代码界面,再点击菜单栏的Navigate) 第二步: 依照第一步的办法,点击测试后,呈现图一 的对话框(如果想要测试的类曾经存在测试类了会被列出来,也能够从新创立一个新的测试类),点击”Create New Test…”会弹出图二 的对话框,能够抉择是否生成setUp以及要测试的成员办法等: 第三步: 至此Service层的测试类就创立好了,测试类主动生成到了src/test/java目录下我的项目的同级目录中 ,如下图: Service层测试代码如下: @SpringBootTest@RunWith(SpringRunner.class)public class XXXServiceTest {@Resourceprivate XXXService XXXService;@Testpublic void conflictTime() { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDate start = LocalDate.parse("2020-10-26", dtf); LocalDate end = LocalDate.parse("2020-10-31", dtf); Integer integer = XXXService.ConflictTime("10000001", start, end); Assert.assertThat(integer, Matchers.notNullValue());//assertThat断言前面介绍 }}注解解释: ...

February 27, 2023 · 2 min · jiezi

关于java:正式抛弃-FeignSpring-6-推出新特性HTTP-Interface这波太秀了

起源:https://juejin.cn/post/717327... 近期,Spring 6 的第一个 GA 版本公布了,其中带来了一个新的个性——HTTP Interface。这个新个性,能够让开发者将 HTTP 服务,定义成一个蕴含特定注解标记的办法的 Java 接口,而后通过对接口办法的调用,实现 HTTP 申请。看起来很像应用 Feign 来实现近程服务调用,上面咱们参考官网文档来实现一个 Demo。 实现一个 Demo首先创立一个简略的 HTTP 服务,这一步能够创立一个简略的 Spring Boot 工程来实现。 先创立一个实体类: public class User implements Serializable { private int id; private String name; // 省略构造方法、Getter和Setter @Override public String toString() { return id + ":" + name; }}再写一个简略的 Controller: @GetMapping("/users")public List<User> list() { return IntStream.rangeClosed(1, 10) .mapToObj(i -> new User(i, "User" + i)) .collect(Collectors.toList());}确保启动服务之后,可能从http://localhost:8080/users地址获取到一个蕴含十个用户信息的用户列表。 上面咱们新建一个 Spring Boot 工程。Spring Boot 根底就不介绍了,举荐看这个收费教程: ...

February 27, 2023 · 2 min · jiezi

关于java:深入理解Java虚拟机第三版-第2章-Java内存区域与内存溢出异常

Run Time Data Area(运行时数据区) Program Counter Register(程序计数器)程序计数器是一块较小的内存区域,它能够看作是以后线程所执行的字节码的行号指示器。作用字节码解释器通过改变程序计数器来顺次读取指令,从而实现代码的流程管制,如:程序执行、抉择、循环、异样解决。在多线程的状况下,程序计数器用于记录以后线程执行的地位,从而当线程被切换回来的时候可能晓得该线程上次运行到哪儿了。留神程序计数器是惟一一个不会呈现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创立而创立,随着线程的完结而死亡。当执行Java办法时,程序计数器纪录的是正在执行的虚拟机字节码指令的行号。当执行Native办法时,程序计数器的值为空(Undefined)Java Virtual Machine Stacks(虚拟机栈)作用为线程提供了一块公有的内存空间,用于存储办法的局部变量、操作数栈、动静链接、办法进口等信息。每个线程都有本人的Java虚拟机栈,随着线程的创立而创立,随着线程的销毁而销毁,生命周期与线程雷同。栈帧Java虚拟机栈中的每个栈帧(Stack Frame)对应着每个办法的调用,每个栈帧由以下四局部组成: Local Variables (局部变量表)次要寄存了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象援用(reference 类型,它不同于对象自身,可能是一个指向对象起始地址的援用指针,也可能是指向一个代表对象的句柄或其余与此对象相干的地位)。 Opreand Stacks (操作数栈)操作数栈用于存储计算过程中的两头后果,例如将两个变量相加的后果,它是一个后进先出(LIFO)的栈构造。操作数栈中的每个元素能够是Java根本数据类型或者一个对象的援用。 Dynamic Linking (动静链接)次要服务一个办法须要调用其余办法的场景。在 Java 源文件被编译成字节码文件时,所有的变量和办法援用都作为符号援用(Symbilic Reference)保留在 Class 文件的常量池里。当一个办法要调用其余办法,须要将常量池中指向办法的符号援用转化为其在内存地址中的间接援用。动静链接的作用就是为了将符号援用转换为调用办法的间接援用。 Return Address (返回地址)返回地址实际上是一个指向代码行号的指针,用于标识办法返回后须要执行的下一条指令。当办法调用完结时,Java虚构机会依据返回地址返回到调用该办法的地位,持续执行程序。 留神虚拟机栈的StackOverFlowError: 若栈的内存大小不容许动静扩大,那么当线程申请栈的深度超过以后 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 谬误。虚拟机栈的OutOfMemoryError: 如果栈的内存大小能够动静扩大, 如果虚拟机在动静扩大栈时无奈申请到足够的内存空间,则抛出OutOfMemoryError异样。Java 办法有两种返回形式,一种是 return 语句失常返回,一种是抛出异样。不论哪种返回形式,都会导致栈帧被弹出。也就是说, 栈帧随着办法调用而创立,随着办法完结而销毁。无论办法失常实现还是异样实现都算作办法完结。Native Method Stack(本地办法栈)本地办法栈和虚拟机栈所施展的作用十分类似,区别是: 虚拟机栈为虚拟机执行 Java 办法 (也就是字节码)服务,而本地办法栈则为虚拟机应用到的 Native 办法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。本地办法被执行的时候,在本地办法栈也会创立一个栈帧,用于寄存该本地办法的局部变量表、操作数栈、动静链接、进口信息。办法执行结束后相应的栈帧也会出栈并开释内存空间,也会呈现 StackOverFlowError 和 OutOfMemoryError 两种谬误。Heap(堆)Java 虚拟机所治理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创立。此内存区域的惟一目标就是寄存对象实例,简直所有的对象实例以及数组都在这里分配内存。Java 世界中“简直”所有的对象都在堆中调配,然而,随着 JIT 编译器的倒退与逃逸剖析技术逐步成熟,栈上调配、标量替换优化技术将会导致一些奥妙的变动,所有的对象都调配到堆上也慢慢变得不那么“相对”了。从 JDK 1.7 开始曾经默认开启逃逸剖析,如果某些办法中的对象援用没有被返回或者未被里面应用(也就是未逃逸进来),那么对象能够间接在栈上分配内存。Java 堆是垃圾收集器治理的次要区域,因而也被称作 GC 堆(Garbage Collected Heap)。从垃圾回收的角度,因为当初收集器根本都采纳分代垃圾收集算法,所以 Java 堆还能够细分为:新生代和老年代;再粗疏一点有:Eden、Survivor、Old 等空间。进一步划分的目标是更好地回收内存,或者更快地分配内存。留神在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常分为新生代、老生代和永恒代,但JDK 8 版本之后 PermGen(永恒) 已被 Metaspace(元空间) 取代,元空间应用的是间接内存 。大部分状况,对象都会首先在 Eden 区域调配,在一次新生代垃圾回收后,如果对象还存活,则会进入 S0 或者 S1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄减少到肯定水平(默认为 15 岁),就会被降职到老年代中。对象降职到老年代的年龄阈值,能够通过参数 -XX:MaxTenuringThreshold 来设置。堆最容易呈现的就是 OutOfMemoryError 谬误:java.lang.OutOfMemoryError: GC Overhead Limit Exceeded : 当 JVM 花太多工夫执行垃圾回收并且只能回收很少的堆空间时,就会产生此谬误。java.lang.OutOfMemoryError: Java heap space :如果在创立新的对象时, 堆内存中的空间不足以寄存新创建的对象, 就会引发此谬误。(和配置的最大堆内存无关,且受制于物理内存大小。最大堆内存可通过-Xmx参数配置,若没有特地配置,将会应用默认值。Method Area(办法区)当虚拟机要应用一个类时,它须要读取并解析 Class 文件获取相干信息,再将信息存入到办法区。办法区会存储已被虚拟机加载的 类信息、字段信息、办法信息、常量、动态变量、即时编译器编译后的代码缓存等数据。留神:办法区在JDK1.8前后的区别? ...

February 27, 2023 · 1 min · jiezi

关于java:三天吃透Java虚拟机面试八股文

本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址:https://github.com/Tyson0314/... 讲一下JVM内存构造?JVM内存构造分为5大区域,程序计数器、虚拟机栈、本地办法栈、堆、办法区。 程序计数器 线程公有的,作为以后线程的行号指示器,用于记录以后虚拟机正在执行的线程指令地址。程序计数器次要有两个作用: 以后线程所执行的字节码的行号指示器,通过它实现代码的流程管制,如:程序执行、抉择、循环、异样解决。在多线程的状况下,程序计数器用于记录以后线程执行的地位,当线程被切换回来的时候可能晓得它上次执行的地位。程序计数器是惟一一个不会呈现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创立而创立,随着线程的完结而死亡。 虚拟机栈 Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都领有:局部变量表、操作数栈、动静链接、办法进口信息。每一次函数调用都会有一个对应的栈帧被压入虚拟机栈,每一个函数调用完结后,都会有一个栈帧被弹出。 局部变量表是用于寄存办法参数和办法内的局部变量。 每个栈帧都蕴含一个指向运行时常量池中该栈所属办法的符号援用,在办法调用过程中,会进行动静链接,将这个符号援用转化为间接援用。 局部符号援用在类加载阶段的时候就转化为间接援用,这种转化就是动态链接局部符号援用在运行期间转化为间接援用,这种转化就是动静链接Java 虚拟机栈也是线程公有的,每个线程都有各自的 Java 虚拟机栈,而且随着线程的创立而创立,随着线程的死亡而死亡。Java 虚拟机栈会呈现两种谬误:StackOverFlowError 和 OutOfMemoryError。 能够通过 -Xss 参数来指定每个线程的虚拟机栈内存大小: java -Xss2M本地办法栈 虚拟机栈为虚拟机执行 Java 办法服务,而本地办法栈则为虚拟机应用到的 Native 办法服务。Native 办法个别是用其它语言(C、C++等)编写的。 本地办法被执行的时候,在本地办法栈也会创立一个栈帧,用于寄存该本地办法的局部变量表、操作数栈、动静链接、进口信息。 堆 堆用于寄存对象实例,是垃圾收集器治理的次要区域,因而也被称作GC堆。堆能够细分为:新生代(Eden空间、From Survivor、To Survivor空间)和老年代。 通过 -Xms设定程序启动时占用内存大小,通过-Xmx设定程序运行期间最大可占用的内存大小。如果程序运行须要占用更多的内存,超出了这个设置值,就会抛出OutOfMemory异样。 java -Xms1M -Xmx2M1.办法区 办法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、动态变量、即时编译器编译后的代码等数据。 对办法区进行垃圾回收的次要指标是对常量池的回收和对类的卸载。 2.永恒代 办法区是 JVM 的标准,而永恒代PermGen是办法区的一种实现形式,并且只有 HotSpot 有永恒代。对于其余类型的虚拟机,如JRockit没有永恒代。因为办法区次要存储类的相干信息,所以对于动静生成类的场景比拟容易呈现永恒代的内存溢出。 3.元空间 JDK 1.8 的时候,HotSpot的永恒代被彻底移除了,应用元空间代替。元空间的实质和永恒代相似,都是对JVM标准中办法区的实现。两者最大的区别在于:元空间并不在虚拟机中,而是应用间接内存。 为什么要将永恒代替换为元空间呢? 永恒代内存受限于 JVM 可用内存,而元空间应用的是间接内存,受本机可用内存的限度,尽管元空间仍旧可能溢出,然而相比永恒代内存溢出的概率更小。 运行时常量池 运行时常量池是办法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动静生成的常量,如 String 类的 intern()办法,也会被放入运行时常量池。 间接内存 间接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机标准中定义的内存区域,然而这部分内存也被频繁地应用。而且也可能导致 OutOfMemoryError 谬误呈现。 ...

February 26, 2023 · 5 min · jiezi

关于java:线程池的状态

RUNNING能接管新工作并解决已增加的工作线程池一旦创立,就处于RUNNING状态,此时线程池中的工作数为0 SHUTDOWN不接管新工作,但能解决已增加的工作调用线程池的shutdown()接口,线程池由 RUNNING 》 SHUTDOWN STOP线程池处于STOP状态,不接管新工作,不解决已增加的工作,并且会中断正在解决的工作调用线程池的shutdownNow()接口,线程池由 RUNNING or SHUTDOWN 》 STOP TIDYING当所有工作已终止,ctl记录的工作数为0,线程池的状态会变为TIDYING当线程池的状态变为TIDYING状态时,会调用钩子函数terminated(),该办法在ThreadPoolExecutor中是空的,若用户想在线程池变为TIDYING时进行相应的解决,就须要重载terminated()函数当线程池状态为SHUTDOWN,阻塞队列为空,线程池中执行的工作也为空时,由 SHUTDOWN 》 TIDYING当线程池状态为STOP,线程池中执行的工作为空时,由 STOP》 TIDYING TERMINATED线程池彻底终止线程池处于TIDYING状态,调用terminated()时,由 TIDYING 》 TERMINATED ThreadPoolExecutor 源码正文 * The runState provides the main lifecycle control, taking on values: * * 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 * * The numerical order among these values matters, to allow * ordered comparisons. The runState monotonically increases over * time, but need not hit each state. The transitions are: * * RUNNING -> SHUTDOWN * On invocation of shutdown(), perhaps implicitly in finalize() * (RUNNING or SHUTDOWN) -> STOP * On invocation of shutdownNow() * SHUTDOWN -> TIDYING * When both queue and pool are empty * STOP -> TIDYING * When pool is empty * TIDYING -> TERMINATED * When the terminated() hook method has completed * * Threads waiting in awaitTermination() will return when the * state reaches TERMINATED.

February 26, 2023 · 1 min · jiezi

关于java:任何人均可上手的数据库与API搭建平台

编写API可能对于很多后端开发人员来说,并不是什么难事儿,但如果您次要从事前端性能,那么可能还是有一些门槛。 那么有没有工具能够帮忙咱们升高编写API的学习门槛和复杂度呢? 明天就来给大家举荐一个不错的开源工具:APITable APITable是一个面向API的可视化数据库,它实用于所有人,甚至没有编程根底的人。你能够是一名程序员,也能够是PMO,甚至销售、HR等职业的人均可应用。 因为APITable的外围是帮忙用户轻松简便的创立和治理数据库,所以只有您理解相似Excel这样的软件,也不须要如何精通,那么就能够疾速上手来实现一些利用场景,比方,你能够用它来实现项目管理、工作治理、问题治理, 也能够用来治理你的客户关系,再有了根底数据之后,还能用它来创立更有用的BI图表或者Dashboard。 APITable还提供了一个十分有用的性能,就是一键生成API面板。让您设计的数据表疾速的提供API能力,供其余中央应用。 所以您能够将其视为一个集数据库和API提供能力的低代码平台,不须要编写一行代码,就能够实现数据存储和API的实现。 除此之外,APITable还提供一系列企业级的优良性能,包含: 实时合作,容许多用户同时操作,合作数据行可达100K+疾速上手,内置了10+个模版帮忙用户疾速创立利用场景实用好看,根底CRUD,各种字段操作,各种视图类型,一键API面板可扩展性,各种图表、Dashboard、数据列类型、公式、机器人等均可自定义企业级能力,团队治理和组织架构,单点登录,审计,数据备份和导出,水印等如果您感觉这个开源工具还不错的话,能够通过上面链接中转哦 官网地址:https://apitable.com/我的项目地址:https://github.com/apitable/a...如果本篇举荐对您有所帮忙,点个收费的赞和在看,关注我继续取得更多干货举荐哦~ 欢送关注我的公众号:程序猿DD。第一工夫理解前沿行业音讯、分享深度技术干货、获取优质学习资源

February 26, 2023 · 1 min · jiezi

关于java:Quartz-集群Cluster的配置failOver原理

在采纳JDBC-Based JobStore 的前提下,Quartz反对集群部署。每一个Scheduler任务调度服务都能够作为集群中的一个节点,节点之间并不相互通信,失常状况下每一个节点只晓得本人、并不知道其余节点的存在,各节点都通过和同一个数据库通信从而实现集群。 集群部署后,作业能够被任意一个Scheduler节点调度,任一个节点失败或down机后,该节点负责的工作会委派给其余失常节点接管。 Quartz集群的配置Quratz集群通过参数org.quartz.jobStore.isCluster配置,Quartz初始化的过程中参数被StdSchedulerFactory读取之后赋值给JobStoreSupport的成员变量isClustered,从而使以后Scheduler变成Quartz集群的一个节点。 StdSchedulerFactory读取org.quartz.jobStore.isCluster参数的形式有必要说一下,因为在读源码找相应配置的时候还是费了点周折的,最初还是在JobStoreSupport中看到了: /** * <p> * Set whether this instance is part of a cluster. * </p> */ @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setIsClustered(boolean isClustered) { this.isClustered = isClustered; }受那句正文called reflectively启发才找到,原来StdSchedulerFactory是通过反射机制设置的: tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX}); try { setBeanProps(js, tProps); } catch (Exception e) { initException = new SchedulerException("JobStore class '" + jsClass + "' props could not be configured.", e); throw initException; }读取到org.quartz.jobStore下的所有配置后,调用setBeanProps(js, tProps)通过反射机制为JobStore设置了配置文件中所有的相干属性。这种形式起码比一个个属性硬读进来设置要来的优雅多了,也灵便多了。 ...

February 25, 2023 · 1 min · jiezi

关于java:Java-反射

反射Java的反射(reflection)机制是指在程序的运行状态中,能够结构任意一个类的对象,能够理解任意一个对象所属的类,能够理解任意一个类的成员变量和办法,能够调用任意一个对象的属性和办法,这种动静获取程序信息以及动静调用对象的性能称为Java语言的反射机制,反射被视为动静语言的要害 反射的过程类的生命周期通过上述知识点可知,类加载后会在堆中生成一个Class对象作为拜访运行时动态数据的入口,也就是拜访具体Java类的信息,如果便能通过Class对象实现各种操作,如创立类实例,获取类属性等;能够将获取Class对象了解为应用反射机制的开始 反射的特点灵活性高,可在运行时动静获取类实例,罕用于容器治理,形象编程,策略模式以及模板办法模式中性能比间接指定类型要低,须要解析字节码,动静获取类对象获取Class对象全路径名Class.forName("java.lang.String");类名.classString.class对象.getClass()String a = "123"; a.getClass();

February 25, 2023 · 1 min · jiezi

关于java:Synchronized-Lock-ReentrantLock-区别

Synchronized & LockSynchronized 能够用于类,办法,代码块加锁;Lock只能作用于代码块Synchronized 不须要手动获取锁,开释锁,更简略;Lock如果没有unlock()可能会带来一些问题Lock 能够通过lock()判断是否获取到了锁,而Synchronized不能Synchronized & ReentrantLock二者皆为可重入锁Synchronized 基于JVM实现(monitorenter指令);ReentrantLock基于API实现(能够了解为,一个在机器码层,一个在应用层)ReentrantLock性能更加全面,反对偏心非偏心模式,反对中断并放弃获取锁,反对Condition来实现线程之间的调度,更加灵便应用抉择除非须要应用到ReentrantLock的高级性能,尽量应用Synchronized,更简略,JVM原生反对Synchronized 锁的降级降级程序为 无锁 》 偏差锁 》 轻量锁 》 分量锁 Synchronized 应用的锁寄存在Java对象头的MarkWord里,其中存储了同步状态、标识、hashcode、GC状态等信息 偏差锁少数状况下,锁的应用并不存在多线程竞争,并且总是由同一个线程取得;因而引入偏差锁,升高线程首次取得锁的代价;偏差锁会偏差于第一个拜访锁的线程,在程序运行中,只有一个线程拜访同步锁,不存在多线程争用的状况,则线程不须要触发同步,间接给该线程加一个偏差锁轻量锁当产生锁的竞争时,偏差锁会降级为轻量级锁,轻量锁是自旋锁;轻量锁的设计思维是,如果持有锁的线程可能在很短时间内开释锁,则期待的线程能够不作上下文切换,只是进行数次自旋期待,持有锁的线程开释锁后即可立刻获取锁,从而防止上下文切换的开销(上下文切换比执行CPU指令的开销要大的多)分量锁线程自旋是须要占用CPU的,因而须要设定一个自旋期待的最大工夫,当持有锁的线程执行的工夫超过自旋期待的最大工夫扔没有开释锁,则竞争锁的线程会进行自旋进入阻塞状态,此时降级到重量级锁,JDK1.6中引入了适应性自旋锁,自旋的工夫不再固定,而是由上一次在同一个锁上的自旋工夫以及锁的拥有者的状态来决定,根本认为是一个线程上下文切换的工夫参考资料:https://blog.csdn.net/m0_6965...

February 25, 2023 · 1 min · jiezi

关于java:我有一篇Java-Stream使用手册学了就是你的了

原创:扣钉日记(微信公众号ID:codelogs),欢送分享,非公众号转载保留此申明。简介日常编程工作中,Java汇合会常常被应用到,且常常须要对汇合做一些相似过滤、排序、对象转换之类的操作。 为了简化这类操作,Java8增加了一套新的Stream API,应用形式就像写SQL一样,大大简化了这类解决的实现代码量与可读性。 根底Stream函数比方,咱们要查问双11期间交易额最大的10笔订单的用户信息,用SQL实现的话,大抵如下: select user_id, user_name from order where pay_time >= '2022-11-01' and pay_time < '2022-12-01' order by goods_amount desc limit 10;这种解决逻辑,不必Stream API,实现代码大抵如下: public static List<User> getTop10Users() throws ParseException { List<Order> orders = getOrders(); // 过滤出双11订单 List<Order> filteredOrders = new ArrayList<>(); long begin = DateUtils.parseDate("2022-11-01", "yyyy-MM-dd").getTime(); long end = DateUtils.parseDate("2022-12-01", "yyyy-MM-dd").getTime(); for (Order order : orders) { if(order.getPayTime().getTime() >= begin && order.getPayTime().getTime() < end) { filteredOrders.add(order); } } // 按订单金额倒序排序 filteredOrders.sort(Comparator.comparing(Order::getGoodsAmount).reversed()); // 取前10名订单,组装出用户信息 List<User> users = new ArrayList<>(); Iterator<Order> it = filteredOrders.iterator(); for (int i = 0; i < 10 && it.hasNext(); i++) { Order order = it.next(); users.add(new User(order.getUserId(), order.getUserName())); } return users;}下面代码与SQL的逻辑是一样的,但能够发现,下面代码的可了解性比SQL差很多,起因是SQL应用的是含意更加靠近用意的申明式语法,而上述代码如果没有很好的正文的话,则须要你的大脑像CPU一样,将各种指令执行一遍才明确大略用意。 ...

February 25, 2023 · 8 min · jiezi

关于java:良许也成为砖家啦

大家好,我是良许。 没错,良许成为砖家啦,绝不是口嗨,有图有假相! 有人会说,咦,这明明是严宇啊,跟你良许有啥关系? 额。。老读者应该晓得良许的来历—— 鄙人真名严宇,当地保留字号的传统,我字良许,号文胜,因为比拟喜爱「良许」二字,所以就以「良许」行走江湖~此时此刻,上面这个表情包十分应景: 好了,貌似一结尾就扯远了…… 那这个砖家证是个什么货色呢? 这是机械工业出版社给我发的专家委员会委员证书,他们这个委员会逼格还是十分高的,有很多都是院士级别的。 而我这个不出名的小辈,当初竟然也跟院士分庭抗礼。。 逼格霎时被拉满! 有些小伙伴可能会好奇,良许是怎么取得到这个证书的呢? 其实在去年,机械工业出版社在 Linux 畛域寻找相干的合作者,而我刚好是 Linux 畛域的头部大号,所以他们就找到我,邀请我一起深耕这一畛域。 通过多轮的钻研和探讨,初步定了一个选题方向,由我牵头,率领团队进行这个选题的编写、测试、修改等一系列工作。 过后破费了很大精力去做这件事儿,甚至连日常的一些诸如直播的工作都停滞了。 之后没多久,我就收到机工社专家委员会的邀请函,于是……貌似我就成砖家了。 那么我有了这个砖家证书后须要做啥呢? 其实简略来说,这个班子很大,定期会召开研讨会,沙龙,教改,有些是有出版单干,有些只是内容单干,有些就是审核单干,有些就相似参谋之类的。 有了这个证书后,我就有资格去加入这些流动啦,对于扩充我的集体影响力还是十分有帮忙的。 至多,我当前能够跟他人吹牛我是砖家了【笑脸】 讲到这里我就略微跟大家聊聊由我牵头的那本书。 这是一本对于 Linux 的入门书籍,面向 Linux 初学者或开源爱好者,在书中特意虚构了一个我的卡通形象,由这个虚构形象贯通全书进行常识的串联,书中不光案例多,还把局部干燥的技术常识以图解的模式进行展现,从而率领读者敌人学习 Linux 。 这种轻松和互动的模式对于初学者来说,更容易接受,学起来也更轻松一些。 而这本也将会在往年面世,到时候出版的时候必定会在公众号送书一波,大家记得来捧场哦~ 学习编程,千万不要急于求成,肯定要多读一些经典书籍,多看源码,多下苦功夫去死磕代码,这样技术能力出息。给大家分享一些程序员必读经典书籍,肯定要多读几遍: 收费送给大家,只求大家金指给我点个赞! 程序员必读经典书单(高清PDF版) 有播种?心愿老铁们来个三连击,给更多的人看到这篇文章举荐浏览: 干货 | 程序员进阶架构师必备资源免费送刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!欢送关注我的博客:良许Linux教程网,满满都是干货!

February 24, 2023 · 1 min · jiezi

关于java:7min到40sSpringBoot启动优化实践

大家好,我是不才陈某~ 公司 SpringBoot 我的项目在日常开发过程中发现服务启动过程异样迟缓,经常须要6-7分钟能力裸露端口,重大升高开发效率。通过 SpringBoot 的 SpringApplicationRunListener 、BeanPostProcessor 原理和源码调试等伎俩排查发现,在 Bean 扫描和 Bean 注入这个两个阶段有很大的性能瓶颈。 关注公众号:码猿技术专栏,回复关键词:1111 获取阿里外部Java性能调优手册通过 JavaConfig 注册 Bean, 缩小 SpringBoot 的扫描门路,同时基于 Springboot 主动配置原理对第三方依赖优化革新,将服务本地启动工夫从7min 降至40s 左右的过程。 本文会波及以下知识点: 基于 SpringApplicationRunListener 原理察看 SpringBoot 启动 run 办法;基于 BeanPostProcessor 原理监控 Bean 注入耗时;SpringBoot Cache 自动化配置原理;SpringBoot 自动化配置原理及 starter 革新;1. 耗时问题排查SpringBoot 服务启动耗时排查,目前有2个思路: 排查 SpringBoot 服务的启动过程;排查 Bean 的初始化耗时;1.1 察看 SpringBoot 启动 run 办法该我的项目应用基于 SpringBoot 革新的外部微服务组件 XxBoot 作为服务端实现,其启动流程与 SpringBoot 相似,分为 ApplicationContext 结构和 ApplicationContext 启动两局部,即通过构造函数实例化 ApplicationContext 对象,并调用其 run 办法启动服务: public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}ApplicationContext 对象结构过程,次要做了自定义 Banner 设置、利用类型推断、配置源设置等工作,不做非凡扩大的话,大部分我的项目都是差不多的,不太可能引起耗时问题。通过在 run 办法中打断点,启动后很快就运行到断点地位,也能验证这一点。 接下就是重点排查 run 办法的启动过程中有哪些性能瓶颈?SpringBoot 的启动过程非常复杂,庆幸的是 SpringBoot 自身提供的一些机制,将 SpringBoot 的启动过程划分了多个阶段,这个阶段划分的过程就体现在 SpringApplicationRunListener 接口中,该接口将 ApplicationContext 对象的 run 办法划分成不同的阶段: ...

February 24, 2023 · 5 min · jiezi

关于java:Java-踩坑小知识集散地

@Resource @Lazy 不兼容须要应用@Autowired + @Lazy SpringBoot 部署动态资源<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>resource目录下新建 static文件夹将动态资源放入 static文件夹 通过 域名/文件名 进行拜访 如 resource/static/1.txtresource/static/test/2.txt则通过以下地址拜访 www.xxx.com/1.txtwww.xxx.com/test/2.txt计算运行时对象占用内存lucene提供的用于计算堆内存占用大小工具类:RamUsageEstimator <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>4.0.0</version></dependency>//计算指定对象及其援用树上的所有对象的综合大小,单位字节long RamUsageEstimator.sizeOf(Object obj)//计算指定对象自身在堆空间的大小,单位字节long RamUsageEstimator.shallowSizeOf(Object obj)//计算指定对象及其援用树上的所有对象的综合大小,返回可读的后果,如:2KBString RamUsageEstimator.humanSizeOf(Object obj)Swagger 测试接口增加 Header@ApiImplicitParams(value = {@ApiImplicitParam(paramType = "header", name = "access-token", required = true)

February 24, 2023 · 1 min · jiezi

关于java:8000字就说一个字Volatile

简介volatile是Java提供的一种轻量级的同步机制。Java 语言蕴含两种外在的同步机制:同步块(或办法)和 volatile 变量,相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。然而volatile 变量的同步性较差(有时它更简略并且开销更低),而且其应用也更容易出错。 Java volatile关键字用于将Java变量标记为“存储在主存储器中”。更确切地说,这意味着,每次读取一个volatile变量都将从计算机的主内存中读取,而不是从CPU缓存中读取,并且每次写入volatile变量都将写入主内存,而不仅仅是CPU缓存。 实际上,自Java 5以来,volatile关键字保障的不仅仅是向主存储器写入和读取volatile变量。我将在以下局部解释。 个性能够把对volatile变量的单个读/写,看成是应用同一个锁对这些单个读/写操作做了同步当咱们申明共享变量为volatile后,对这个变量的读/写将会很特地。了解volatile个性的一个好办法是:把对volatile变量的单个读/写,看成是应用同一个锁对这些单个读/写操作做了同步。 COPYclass VolatileFeaturesExample { //应用volatile申明64位的long型变量 volatile long vl = 0L; public void set(long l) { vl = l; //单个volatile变量的写 } public void getAndIncrement () { vl++; //复合(多个)volatile变量的读/写 } public long get() { return vl; //单个volatile变量的读 }}假如有多个线程别离调用下面程序的三个办法,这个程序在语义上和上面程序等价:COPYclass VolatileFeaturesExample { long vl = 0L; // 64位的long型一般变量 //对单个的一般 变量的写用同一个锁同步 public synchronized void set(long l) { vl = l; } public void getAndIncrement () { //一般办法调用 long temp = get(); //调用已同步的读办法 temp += 1L; //一般写操作 set(temp); //调用已同步的写办法 } public synchronized long get() { //对单个的一般变量的读用同一个锁同步 return vl; }}如下面示例程序所示,对一个volatile变量的单个读/写操作,与对一个一般变量的读/写操作应用同一个锁来同步,它们之间的执行成果雷同。 ...

February 24, 2023 · 3 min · jiezi

关于java:一文详解-Netty-组件

作者:京东物流 张弓言 一、背景Netty 是一款优良的高性能网络框架,外部通过 NIO 的形式来解决网络申请,在高负载下也能牢靠和高效地解决 I/O 操作 作为较底层的网络通信框架,其被广泛应用在各种中间件的开发中,比方 RPC框架、MQ、Elasticsearch等,这些中间件框架的底层网络通信模块大都利用到了 Netty 弱小的网络形象 上面这篇文章将次要对 Netty 中的各个组件进行剖析,并在介绍完了各个组件之后,通过 JSF 这个 RPC 框架为例来剖析 Netty 的应用,心愿让大家对 Netty 能有一个清晰的理解 二、Netty Server通过 Netty 来构建一个繁难服务端是比较简单的,代码如下: public class NettyServer { public static final Logger LOGGER = LoggerFactory.getLogger(NettyServer.class); public static void main(String[] args) { ServerBootstrap serverBootstrap = new ServerBootstrap(); EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); ChannelFuture channelFuture = serverBootstrap .group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childOption(ChannelOption.SO_KEEPALIVE, true) .handler(new ChannelHandlerAdapter() { @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { LOGGER.info("Handler Added"); } }) .childHandler(new ServerChannelInitializer()) .bind(8100); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { LOGGER.info("Netty Server Start !"); } } }); try { channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } }}下面代码的次要逻辑如下: ...

February 24, 2023 · 6 min · jiezi

关于java:一个诡异的-Pulsar-InterruptedException-异常

背景明天收到业务团队反馈线上有个利用往 Pulsar 中发送音讯失败了,通过日志查看得悉是发送音讯时候抛出了 java.lang.InterruptedException 异样。 和业务沟通后得悉是在一个 gRPC 接口中触发的音讯发送,大概继续了半个小时的异样后便恢复正常了,这是整个问题的背景。 前置排查拿到该问题后首先排查下是否是共性问题,查看了其余的利用没有发现相似的异样;同时也查看了 Pulsar broker 的监控大盘,在这个时间段仍然没有稳定和异样; 这样能够初步排除是 Pulsar 服务端的问题。 接着便是查看利用那段时间的负载状况,从利用 QPS 到 JVM 的各个内存状况仍然没发现有什么显著的变动。 Pulsar 源码排查既然看起来利用自身和 Pulsar broker 都没有问题的话那就只能从异样自身来排查了。 首先第一步要得悉具体应用的是 Pulsar-client 是版本是多少,因为业务应用的是外部基于官网 SDK 封装 springboot starter 所以第一步还得排查这个 starter 是否有影响。 通过查看源码根本排除了 starter 的嫌疑,外面只是简略的封装了 SDK 的性能而已。 org.apache.pulsar.client.api.PulsarClientException: java.util.concurrent.ExecutionException: org.apache.pulsar.client.api.PulsarClientException: java.lang.InterruptedException at org.apache.pulsar.client.api.PulsarClientException.unwrap(PulsarClientException.java:1027) at org.apache.pulsar.client.impl.TypedMessageBuilderImpl.send(TypedMessageBuilderImpl.java:91) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: java.util.concurrent.ExecutionException: org.apache.pulsar.client.api.PulsarClientException: java.lang.InterruptedException at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395) at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999) at org.apache.pulsar.client.impl.TypedMessageBuilderImpl.send(TypedMessageBuilderImpl.java:89) ... 49 common frames omitted Caused by: org.apache.pulsar.client.api.PulsarClientException: java.lang.InterruptedException at org.apache.pulsar.client.impl.ProducerImpl.canEnqueueRequest(ProducerImpl.java:775) at org.apache.pulsar.client.impl.ProducerImpl.sendAsync$original$BWm7PPlZ(ProducerImpl.java:393) at org.apache.pulsar.client.impl.ProducerImpl.sendAsync$original$BWm7PPlZ$accessor$i7NYMN6i(ProducerImpl.java) at org.apache.pulsar.client.impl.ProducerImpl$auxiliary$EfuVvJLT.call(Unknown Source) at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86) at org.apache.pulsar.client.impl.ProducerImpl.sendAsync(ProducerImpl.java) at org.apache.pulsar.client.impl.ProducerImpl.internalSendAsync(ProducerImpl.java:292) at org.apache.pulsar.client.impl.ProducerImpl.internalSendWithTxnAsync(ProducerImpl.java:363) at org.apache.pulsar.client.impl.PartitionedProducerImpl.internalSendWithTxnAsync(PartitionedProducerImpl.java:191) at org.apache.pulsar.client.impl.PartitionedProducerImpl.internalSendAsync(PartitionedProducerImpl.java:167) at org.apache.pulsar.client.impl.TypedMessageBuilderImpl.sendAsync(TypedMessageBuilderImpl.java:103) at org.apache.pulsar.client.impl.TypedMessageBuilderImpl.send(TypedMessageBuilderImpl.java:82) ... 49 common frames omitted Caused by: java.lang.InterruptedException: nullat java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1343) at java.base/java.util.concurrent.Semaphore.acquire(Semaphore.java:318) at org.apache.pulsar.client.impl.ProducerImpl.canEnqueueRequest(ProducerImpl.java:758)接下来便只能是剖析堆栈了,因为 Pulsar-client 的局部实现源码是没有间接打包到依赖中的,反编译的话许多代码行数对不上,所以须要将官网的源码拉到本地,切换到对于的分支进行查看。 ...

February 24, 2023 · 2 min · jiezi

关于java:JavaThe-Java-Headless-Mode

原文https://www.baeldung.com/java-headless-mode 引言这篇文章源自集体看到了Kafka的启动脚本中一个“奇怪”的参数: -Djava.awt.headless=true拿去谷歌一下发现网上的形容都大差不差,这里找了baeldung(相似国外的菜鸟教程)中的一篇文章,本文内容来自于英文博客原文。 这篇文章介绍了 -Djava.awt.headless 参数的作用,网上大部分的材料都是说“为了进步计算效率和适配性咱们能够应用这种模式,敞开图形显示等性能能够大大节俭设施的计算能力,而且对一些自身没有相干显示设施的机器也能适配,程序也能够失常运行。”,集体认为这些实践内容不太能了解。 当然也有诸如服务器没有显示屏什么的,你得通知程序一声,你工作的中央没有这些设施这种说法 ,为此找了一篇国外的博客介绍。 如何设置?设置形式如下: 在system property中设置 _java.awt.headless_ 为 _true_。SpringBoot的源码中能够找到相似的代码: private void configureHeadlessProperty() { System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless))); }启动脚本中进行设置-Djava.awt.headless=true:在Kafka的脚本当中存在相似的启动脚本。KAFKA_JVM_PERFORMANCE_OPTS="-server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -XX:MaxInlineLevel=15 -Djava.awt.headless=true"最初一个参数显示它应用headless模式。 在执行命令的时候动静增加-Djava.awt.headless=true,这种形式和脚本设置启动的形式相似。Headless 绕过重量级组件如果一个带有GUI组件的代码在开和关Headless模式下运行别离会有什么不同的成果? @Test public void FlexibleApp() { if (GraphicsEnvironment.isHeadless()) { System.out.println("Hello World"); } else { JOptionPane.showMessageDialog(null, " showMessageDialog Hello World"); } }下面的代码如果敞开了Headless模式,则打印Hello World会变为图形化界面。 如果开启Headless,则会打印在控制台。 Headless Mode 在UI组件的利用案例Java Headless Mode 的典型案例可能是应用图形转化器,咱们有时候可能须要图形数据进行图像处理,然而不肯定须要理论显示。 上面通过一个单元测试来模仿这些状况: @Before public void setUpHeadlessMode() { // 通过正文掉上面的代码测试不同的成果 // System.setProperty("java.awt.headless", "true"); } @Test public void whenSetUpSuccessful_thenHeadlessIsTrue() { boolean headless = GraphicsEnvironment.isHeadless(); Assert.assertTrue(headless); }/* 测试通过 正文上面的代码之后,单元测试不通过 // System.setProperty("java.awt.headless", "true"); */应用awt的组件java.awt.GraphicsEnvironment#isHeadless,留神较高版本的JDK(例如 JDK11)中awk被间接干掉了,须要下载内部依赖导入才能够应用,倡议抉择JDK8以及以下的版本测试下面的程序。 ...

February 23, 2023 · 2 min · jiezi

关于java:Java单元测试浅析JUnitMockito

作者:京东物流 秦彪 1. 什么是单元测试(1)单元测试环节: 测试过程依照阶段划分分为:单元测试、集成测试、零碎测试、验收测试等。相干含意如下: 1)       单元测试: 针对计算机程序模块进行输入正确性测验工作。 2)       集成测试: 在单元测试根底上,整合各个模块组成子系统,进行集成测试。 3)       零碎测试: 将整个交付所波及的合作内容都纳入其中思考,蕴含计算机硬件、软件、接口、操作等等一系列作为一个整体,测验是否满足软件或需要阐明。 4)       验收测试: 在交付或者公布之前对所做的工作进行测试测验。 单元测试是阶段性测试的首要环节,也是白盒测试的一种,该内容的编写与实际能够前置在研发实现,研发在编写业务代码的时候就须要生成对应代码的单元测试。单元测试的发起人是程序设计者,受益人也是编写程序的人,所以对于程序员,十分有必要造成自我约束力,实现根本的单元测试用例编写。 (2)单元测试特色: 由上可知,单元测试其实是针对软件中最小的测试单元来进行验证的。这里的单元就是指相干的性能子集,比方一个办法、一个类等。值得注意的是作为最低级别的测试流动,单元测试验证的对象仅限于以后测试内容,与程序其它局部内容相隔离,总结起来单元测试有以下特色: 1)       次要性能是证实编写的代码内容与冀望输入统一。 2)       最小最低级的测试内容,由程序员本身发动,保障程序根本组件失常。 3)       单元测试尽量不要区分类与办法,主张以过程性的办法为测试单位,简略实用高效为指标。 4)       不要偏离主题,专一于测试一小块的代码,保障根底性能。 5)       剥离与内部接口、存储之间的依赖,使单元测试可控。 6)       任何工夫任何程序执行单元测试都须要是胜利的。 2. 为什么要单元测试(1)单元测试意义: 程序代码都是由根本单元一直组合成简单的零碎,底层根本单元都无奈保障输入输出正确性,层级递增时,问题就会一直放大,直到整个零碎解体无奈应用。所以单元测试的意义就在于保障基本功能是失常可用且稳固的。而对于接口、数据源等起因造成的不稳固因素,是外在起因,不在单元测试思考范畴之内。 (2)应用main办法进行测试: @PostMapping(value="/save")public Map<String,Object> save(@RequestBody Student stu) { studentService.save(stu);    Map<String,Object> params = new HashMap<>();    params.put("code",200);    params.put("message","保留胜利");    return params;}如果要对下面的Controller进行测试,能够编写如下的代码示例,应用main办法进行测试的时候,先启动整个工程利用,而后编写main办法如下进行拜访,在单步调试代码。 public static void main(String[] args) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); String json = "{"name":"张三","className":"三年级一班","age":"20","sex":"男"}"; HttpEntity<String> httpEntity = new HttpEntity<>(json, headers); String url = "http://localhost:9092/student/save"; MainMethodTest test = new MainMethodTest(); ResponseEntity<Map> responseEntity = test.getRestTemplate().postForEntity(url, httpEntity, Map.class); System.out.println(responseEntity.getBody()); }(3)应用main办法进行测试的毛病: ...

February 23, 2023 · 4 min · jiezi

关于java:Spark-集群执行任务失败的故障处理

昨天(2023-02-22)开始发现公司 Spark 集群上呈现一些工作执行工夫过长最初失败,具体表现包含: 大量执行失败的 Task,最终工作也是失败的在 Spark Master 治理界面上看到工作的 Driver 地址不是实在 IP 地址,而是一个叫做“host.containers.internal”的主机名;Spark 的 worker 节点上能察看到在不停的创立 Java 过程,而后过程霎时就完结了;进入 worker 节点的日志目录查看日志内容,发现异常信息为连贯 “host.containers.internal” 这个地址失败。所以显然以后呈现的问题跟“host.containers.internal”有关系。 背景阐明:咱们的 Spark 集群是运行在 podman 容器里的,而且是在非 root 用户下运行。 通过在互联网上搜寻,发现这个主机名是容器调配给外部过程用来连贯容器所在主机本身的。再进一步查看 podman 参考文档,依照外面的说法,仅当容器运行网络模式为 slirp4netns,即带上参数 "--network=slirp4netns" 时,才会有 host.containers.internal 这个主机名。 但我运行容器时带的参数是 "--network=host" 啊。 再认真看文档才晓得,slirp4netns 模式是非 root 运行容器的默认模式。依照我遇到的理论状况,难道我给的 "--network=host" 参数并没有起作用?然而用 podman inspect xxx | grep NetworkMode 命令查看容器失去的后果是: "NetworkMode": "host"不懂,先把这个放到一边,那么如何拜访 host.containers.internal 这个主机呢,有两种形式: 参数改为 "--network=slirp4netns:allow_host_loopback=true"批改 /usr/share/containers/containers.conf,批改或增加配置 network_cmd_options 的值为 ["allow_host_loopback=true"]在不批改 --network 参数的前提下,我用第二种办法试试。 批改配置文件而后重启各个 worker 容器,故障隐没,Spark 工作可能顺利执行实现。但还须要察看一段时间。

February 23, 2023 · 1 min · jiezi

关于java:Quartz-JDBCBased-JobStore事务管理及锁机制

因为JDBC-Based JobStore在进行job注册、trigger注册、任务调度及执行过程中须要操作数据库,而且会波及到多张表,比方trigger注册的时候会依据不同状况写入triggers、simple_triggers或cron_triggers表,在执行工作的时候会读取和更新triggers、job_details、simple_triggers、cron_triggers、fired_triggers等。这些操作都有事务性要求:要么全副胜利、要么全副失败,否则就会导致数据不统一,最终会影响到工作的正确调度和执行。 Quartz的事务管理JDBC-Based JobStore有两个JobStore的最终实现类,一个是JobStoreTX,一个是JobStoreCMT,都继承自抽象类JobStoreSupport。 这两个实现类都是为JDBC-Based JobStore提供事务管理能力的,其中JobStoreTX是本人实现事务管理的,事务的开启、commit、rollback都由JobStoreTX管制。 JobStoreCMT是依赖于容器来治理事务的,他把事务管理的职责交给了运行环境,他本人自身不做事务管理。比方能够交给Spring来进行事务管理。 具体应用哪一个JobStore是通过Quartz的配置文件指定的: org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX配置为JobStoreCMT的状况下须要在配置文件中额定指定nonManagedTXDataSource:不受Quartz治理的事务的数据源。 两种事务管理机制都反对一个属性:dontSetAutoCommitFalse,字面含意是不容许设置数据库连贯的autoCommit为false,理论含意就是不容许开启事务。这个参数的默认值为false:容许开启事务,在这个默认设置下,Quartz在获取到数据库连贯后会设置其autoCommit为false,也就是相当于开启了事务,个别状况下这个参数也不须要批改。 然而如果是采纳JobStoreCMT、交给容器治理事务的话,应该是能够设置dontSetAutoCommitFalse为true从而彻底交给容器来启用、commit、rollback事务的,这部分内容有待验证! Quartz的锁机制启用事务管理之后是不是就能够十拿九稳的确保工作的正确执行呢?在多任务并发、或者在cluster的环境下,并发工作可能存在同时拜访同一条数据的可能,仅仅是事务管理还不足以确保工作的正确执行,还须要引入锁机制。 Quartz提供了一个叫Semaphore的接口来实现锁,Semaphore接口有obtainLock、releaseLock、requiresConnection 3个办法,别离用来获取锁、开释锁、以及判断以后锁对象在执行锁操作的时候是否须要数据库连贯的反对。 从Semaphore的类构造能够看到他有两个不同的实现类: SimpleSemaphore:基于内存的锁机制DBSemaphore:基于数据库的锁机制具体采纳哪种类型的锁能够通过配置文件指定:org.quartz.jobStore.lockHandler.class=SimpleSemaphore 两种锁机制的区别基于内存的锁机制能够称之为“轻量级锁”,操作速度快、资源占用少、锁等待时间短,不须要底层数据库的反对。然而基于内存的锁机制不能实现跨利用的锁,在集群环境下基于内存的锁机制无奈实现目标。 比照而言,基于数据库的锁是“重量级锁”,通过给数据库表(qrtz_lock)的某一行或者整张表加锁从而实现以后线程对资源的锁定。基于数据库的锁能够反对集群环境。 加锁与不加锁操作JobStoreSupport中提供了两种数据库操作方法: executeInLock:加锁操作数据库executeWithoutLock:不加锁操作数据库这是因为加锁操作数据库的时候会造成想要获取同一信息的其余线程的锁期待,轻则影响性能,重则造成操作超时从而影响工作的失常调度执行。所以,Quartz就提供了这两种数据库操作,只给那些对数据十分敏感的操作加锁,非必要的状况下就不加锁。 比方在注册job和trigger的时候就加锁,因为注册操作并不是一个数据库操作、而是一系列数据库操作,只有所有的注册操作实现之后,能力容许调度工作开始调度该作业,所以注册操作必须加锁执行。 而某些查问性能比方getTriggerState、retrieveTrigger等等就不须要加锁,所以采纳不加锁形式拜访数据库,无疑会进步性能、无效防止锁超时、进步利用性能。 Quartz的锁对象从Quartz须要拜访的资源来看,须要上锁的有两种: TRIGGER_ACCESS:也就是拜访TRIGGER的时候须要上锁,这也比拟容易了解,因为不论是作业的注册、还是调度执行,须要频繁操作TRIGGERSTATE_ACCESS:拜访集群服务器状态表(qrtz_scheduler_state)的时候须要上锁,因为集群环境下多个服务器可能须要同时拜访、更新状态表内存锁SimpleSemaphoreQuartz在非集群环境下的默认锁机制为内存锁SimpleSemaphore。 SimpleSemaphore提供一个锁容器(HashSet)locks,以及一个ThreadLocal变量lockOwners。 obtainLock办法:加锁操作前获取锁资源(TRIGGER_ACCESS或STATE_ACCESS)。首先查看lockOwners,如果锁资源曾经被以后线程获取(在lockOwners中)则无需期待、间接返回。否则,查看locks中是否存在该资源,存在的话阐明以后锁资源曾经被其余线程获取,则以后线程需期待锁开释。一旦其余线程开释后,则以后线程将该锁资源存入locks,同时注销lockOwners。 releaseLock办法:加锁操作执行实现后开释锁资源。查看lockOwners如果以后线程曾经锁定该资源的话,则将以后锁资源从lockOwners和locks中移除。 DBSemaphore基于数据库的锁DBSemaphore是抽象类,有两个落地实现类StdRowLockSemaphore和UpdateLockRowSemaphore: StdRowLockSemaphore:通过select ... for update实现行锁,Quartz默认应用UpdateLockRowSemaphore:通过update 语句实现行锁,对于不反对通过select for update加锁的数据库,比方 MS SQLServer,须要采纳UpdateLockRowSemaphoreDBSemaphore的原理其实也非常简单:当Quartz判断某一操作须要锁定资源的时候,首先辨别一下须要行级锁还是表级锁,如果须要行级锁则通过select ... for update锁定qrtz_locks表中的指定行(TRIGGER_ACCESS或STATE_ACCESS),当须要表级锁的时候就应用insert语句锁定整张表。 obtainLock办法:其实就是执行上述行级锁或表级锁的操作,但因为数据库锁的开销比拟大,所以在执行锁定之前首先通过lockOwners判断以后线程是否曾经取得了锁,曾经取得锁的话就不再执行sql语句去取得锁了,节约开销。 releaseLock办法:从lockOwners移除锁资源,数据库锁是不须要显式的操作去开释的,事务提交或回滚之后天然就开释了锁。 小结明天实现了Quratz的事务管理及锁机制的剖析,Quartz的集群治理稍后剖析。 Thanks a lot! 上一篇 Quartz - JDBC-Based JobStore

February 23, 2023 · 1 min · jiezi

关于java:Linux内存管理神器smem工具

大家好,我是良许。 明天给大家分享一款 Linux 零碎里的内存治理神器:smem 。 smem 是Linux零碎上的一款能够生成多种内存耗用报告的命令行工具。与现有工具不一样的是 smem 能够报告 PSS【Proportional Set Size(按比例占用大小)】,这是一种更有意义的指标。能够掂量虚拟内存零碎的库和应用程序所占用的内存数量。 因为大部分的物理内存通常在多个应用程序之间共享,名为驻留集大小(RSS)的这个规范的内存耗用掂量指标会大大高估内存耗用状况。PSS这个参数而是掂量了每个应用程序在每个共享内存区中的「偏心调配」,给出了一个切合实际的掂量指标。 1. 装置 smem 工具如果你应用的是 Fedora 19 以上零碎, smem 默认在存储库中,因而你能够应用 yum 来装置它: $ sudo yum install smem对于 Ubuntu 用户,能够应用 apt-get 命令来装置 smem : $ sudo apt-get install smem如果无奈失常装置的话,能够下载它的源码间接装置,地址为:https://www.selenic.com/smem/... 2. smem 工具常见用法默认状况下, smem 将显示每个正在运行的过程及所应用的内存。在这里,你能够注意 RSS 绝对于 USS 和 PSS 的大小,能够看到它显著高于另外二者。 $ smem 此外, smem 还可显示每个库所应用的内存。这个后果比拟长,可能须要消耗一些工夫,取决于你的零碎。 $ smem -m这个命令产生的后果太多了,如果咱们想要查看特定应用程序应用内存状况,例如 Firefox,那么咱们能够配合 grep 命令应用,同时应用 -p 选项以百分比模式查看内存应用状况。 $ smem -m -p | grep firefox ...

February 22, 2023 · 1 min · jiezi

关于java:GitHub标星30K的Java面试八股文长啥样

2023年的互联网行业竞争越来越严厉,面试也是越来越难,始终以来我都想整顿一套完满的面试宝典,奈何难抽出工夫,这套1000+道的Java面试手册我整顿了整整1个月,上传到Git上目前star数达到了30K+ 一、32 道 MySQL 面试题1:MySQL 的逻辑架构理解吗? 2:谈一谈 MySQL 的读写锁 3:MySQL 的锁策略有什么? 4:数据库死锁如何解决? 5:事务是什么? 6:事务有什么个性? 7:MySQL 的隔离级别有哪些? 8:MVCC 是什么? 9:谈一谈 InnoDB 10:谈一谈 MyISAM 11:谈一谈 Memory 12:查问执行流程是什么? 13:VARCHAR 和 CHAR 的区别? 14:DATETIME 和 TIMESTAMP 的区别? 15:数据类型有哪些优化策略? 16:索引有什么作用? 17:谈一谈 MySQL 的 B-Tree 索引 18:理解 Hash 索引吗? 19:什么是自适应哈希索引? 20 :什么是空间索引? 21:什么是全文索引? 22:什么是聚簇索引? 23:什么是笼罩索引? 24:你晓得哪些索引应用准则? 25:索引生效的状况有哪些? 26:如何定位低效 SQL? 27:SHOW PROFILE 的作用? 28:trace 是干什么的? 29:EXPLAIN 的字段有哪些,具备什么含意? 30:有哪些优化 SQL 的策略? 31:MySQL 主从复制的作用? 32:MySQL 主从复制的步骤? ...

February 22, 2023 · 2 min · jiezi

关于java:Mac安装开发环境应用端篇

前言跟前端篇一样,当初记录一下后盾利用端开发环境的搭建,针对的是java环境,当初分享给大家。 操作1、下载和装置Java下载地址在这下载dmg装置即可,接着查问装置java的地位 /usr/libexec/java_home -V找到java装置地位 之后,当初咱们来编辑.bash_profile文件,增加java环境 #java homeJAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Homeexport CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:.保留完之后,别忘了source ~/.bash_profile 2、下载和装置Maven点击官网下载地址下载下载之后解压到你想要保留的中央即可。 保留完之后,配置一下环境变量,再次编辑~/.bash_profile #java homeJAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Homeexport CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:.# maven homeM3_HOME=/Users/zhangwei/Library/apache-maven-3.5.0export PATH=$JAVA_HOME/bin:$M3_HOME/bin:$PATH:这样就实现了Java和Maven的装置,查看Java和Maven的版本 java --versionmvn -v3、配置maven setting.xml文件先找到maven装置门路,我的门路是 ~/Library/apache-maven-3.5.0/conf/settings.xml找到之后编辑setting.xml文件 <?xml version="1.0" encoding="UTF-8"?><settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <mirrors> <mirror> <id>mirror</id> <mirrorOf>central,jcenter,!rdc-releases,!rdc-snapshots</mirrorOf> <name>mirror</name> <url>https://maven.aliyun.com/nexus/content/groups/public</url> </mirror> </mirrors> <servers> <server> <id>rdc-releases</id> <username>xxx</username> <password>xxx(H</password> </server> <server> <id>rdc-snapshots</id> <username>xxx</username> <password>xxx(H</password> </server> </servers> <profiles> <profile> <id>rdc</id> <properties> <altReleaseDeploymentRepository> rdc-releases::default::https://packages.aliyun.com/maven/repository/2113702-release-6l40oS/ </altReleaseDeploymentRepository> <altSnapshotDeploymentRepository> rdc-snapshots::default::https://packages.aliyun.com/maven/repository/2113702-snapshot-zW0Kdb/ </altSnapshotDeploymentRepository> </properties> <repositories> <repository> <id>central</id> <url>https://maven.aliyun.com/nexus/content/groups/public</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>snapshots</id> <url>https://maven.aliyun.com/nexus/content/groups/public</url> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>rdc-releases</id> <url>https://packages.aliyun.com/maven/repository/2113702-release-6l40oS/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>rdc-snapshots</id> <url>https://packages.aliyun.com/maven/repository/2113702-snapshot-zW0Kdb/</url> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>central</id> <url>https://maven.aliyun.com/nexus/content/groups/public</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>snapshots</id> <url>https://maven.aliyun.com/nexus/content/groups/public</url> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>rdc-releases</id> <url>https://packages.aliyun.com/maven/repository/2113702-release-6l40oS/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>rdc-snapshots</id> <url>https://packages.aliyun.com/maven/repository/2113702-snapshot-zW0Kdb/</url> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <activeProfile>rdc</activeProfile> </activeProfiles></settings>这样就配置好了Maven。 ...

February 22, 2023 · 1 min · jiezi

关于java:98的程序员都没有研究过JVM重排序和顺序一致性

文章整顿自 博学谷狂野架构师重排序数据依赖性如果两个操作拜访同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分下列三种类型:名称代码示例阐明写后读a = 1;b = a;写一个变量之后,再读这个地位。写后写a = 1;a = 2;写一个变量之后,再写这个变量。读后写a = b;b = 1;读一个变量之后,再写这个变量。下面三种状况,只有重排序两个操作的执行程序,程序的执行后果将会被扭转。后面提到过,编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时,会恪守数据依赖性,编译器和处理器不会扭转存在数据依赖关系的两个操作的执行程序。 留神,这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器思考。 as-if-serial语义as-if-serial语义的意思指:不管怎么重排序(编译器和处理器为了进步并行度),(单线程)程序的执行后果不能被扭转。编译器,runtime 和处理器都必须恪守as-if-serial语义。为了恪守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会扭转执行后果。然而,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序。为了具体阐明,请看上面计算圆面积的代码示例: COPYdouble pi = 3.14; //Adouble r = 1.0; //Bdouble area = pi * r * r; //C下面三个操作的数据依赖关系如下图所示: 如上图所示,A和C之间存在数据依赖关系,同时B和C之间也存在数据依赖关系。因而在最终执行的指令序列中,C不能被重排序到A和B的后面(C排到A和B的后面,程序的后果将会被扭转)。但A和B之间没有数据依赖关系,编译器和处理器能够重排序A和B之间的执行程序。下图是该程序的两种执行程序: as-if-serial语义把单线程程序爱护了起来,恪守as-if-serial语义的编译器,runtime 和处理器独特为编写单线程程序的程序员创立了一个幻觉:单线程程序是按程序的程序来执行的。as-if-serial语义使单线程程序员无需放心重排序会烦扰他们,也无需放心内存可见性问题。 程序程序规定依据happens- before的程序程序规定,下面计算圆的面积的示例代码存在三个happens- before关系:COPYA happens- before B;B happens- before C;A happens- before C;这里的第3个happens- before关系,是依据happens- before的传递性推导进去的。 这里A happens- before B,但理论执行时B却能够排在A之前执行(看下面的重排序后的执行程序),如果A happens- before B,JMM并不要求A肯定要在B之前执行。JMM仅仅要求前一个操作(执行的后果)对后一个操作可见,且前一个操作按程序排在第二个操作之前。这里操作A的执行后果不须要对操作B可见;而且重排序操作A和操作B后的执行后果,与操作A和操作B按happens- before程序执行的后果统一。在这种状况下,JMM会认为这种重排序并不非法(not illegal),JMM容许这种重排序。 在计算机中,软件技术和硬件技术有一个独特的指标:在不扭转程序执行后果的前提下,尽可能的开发并行度。编译器和处理器听从这一指标,从happens- before的定义咱们能够看出,JMM同样听从这一指标。 重排序对多线程的影响当初让咱们来看看,重排序是否会扭转多线程程序的执行后果。请看上面的示例代码:COPYclass ReorderExample { int a = 0; boolean flag = false; public void writer() { a = 1; //1 flag = true; //2 } public void reader() { if (flag) { //3 int i = a * a; //4 …… } }}flag变量是个标记,用来标识变量a是否已被写入。这里假如有两个线程A和B,A首先执行writer()办法,随后B线程接着执行reader()办法。线程B在执行操作4时,是否看到线程A在操作1对共享变量a的写入? ...

February 22, 2023 · 1 min · jiezi

关于java:面试官怎么去除-List-中的重复元素我一行代码搞定赶紧拿去用

问题上次栈长给大家分享了《带了一个 3 年的开发,不会循环删除 List 中的元素,我几乎解体!!》,上次也给大家留了个小话题: 怎么去除 List\<String> 中的反复元素呢?尽管两个话题差不多,但实现起来就天壤之别了,废话少说,来看看都有哪些实现形式,这仅是我集体的实现计划,不肯定全,也不肯定是最优的,欢送大家拍砖。 List 去重计划假如有以下数据: /** * 3 个张三,2 个李强 */public List<String> initList = Arrays.asList( "张三", "李四", "张三", "周一", "刘四", "李强", "李白", "张三", "李强", "王五");本文所有残缺示例源代码曾经上传: https://github.com/javastacks...欢送 Star 学习,前面 Java 示例都会在这下面提供! 1、for 循环增加去重/** * for 循环增加去重 * @author: 栈长 * @from: 公众号Java技术栈 */@Testpublic void remove1() { List<String> list = new ArrayList(initList); List<String> list2 = new ArrayList<>(); for (String element : list) { if (!list2.contains(element)) { list2.add(element); } } System.out.println(list2);}这个是最根本的实现了,创立一个空的 List,增加前判断一下存在不存在,不存在才增加,这样就保障了元素不反复。 ...

February 22, 2023 · 2 min · jiezi

关于java:SpringBoot-vue-mysql在线云盘系统源码包安装讲解

项目名称 SpringBoot vue mysql在线云盘零碎源码(包装置+解说) 视频演示 SpringBoot vue mysql在线云盘零碎源码(包装置+解说)_哔哩哔哩_bilibili 零碎介绍 反对对接 S3、OneDrive、SharePoint、又拍云、本地存储、FTP、SFTP 等存储源,反对在线浏览图片、播放音视频,文本文件、Office、obj(3d)等文件类型。 技术栈 后端:Spring+SpringMVC+Mybatis前端:JSP+CSS+JavaScript+jQuery应用阐明 应用Navicat或者其它工具,在mysql中创立对应名称的数据库,并导入我的项目的sql文件;应用IDEA/Eclipse/MyEclipse导入我的项目,Eclipse/MyEclipse导入时,若为maven我的项目请抉择maven;若为maven我的项目,导入胜利后请执行maven clean;maven install命令,而后运行;将我的项目中springmvc-servlet.xml配置文件中的数据库配置改为本人的配置;运行我的项目,在浏览器中输出http://localhost:8080/ 登录运行截图 编辑 编辑 编辑 编辑 编辑 编辑 编辑 编辑 编辑 编辑 编辑 编辑 编辑 编辑  用户管理控制层: package com.houserss.controller; import javax.servlet.http.HttpSession; import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody; import com.houserss.common.Const;import com.houserss.common.Const.Role;import com.houserss.common.ServerResponse;import com.houserss.pojo.User;import com.houserss.service.IUserService;import com.houserss.service.impl.UserServiceImpl;import com.houserss.util.MD5Util;import com.houserss.util.TimeUtils;import com.houserss.vo.DeleteHouseVo;import com.houserss.vo.PageInfoVo; /** Created by admin */@Controller@RequestMapping("/user/")public class UserController { @Autowiredprivate IUserService iUserService;/** * 用户登录 * @param username * @param password * @param session * @return */@RequestMapping(value = "login.do",method = RequestMethod.POST)@ResponseBodypublic ServerResponse<User> login(User user,String uvcode, HttpSession session){ String code = (String)session.getAttribute("validationCode"); if(StringUtils.isNotBlank(code)) { if(!code.equalsIgnoreCase(uvcode)) { return ServerResponse.createByErrorMessage("验证码不正确"); } } ServerResponse<User> response = iUserService.login(user.getUsername(),user.getPassword()); if(response.isSuccess()){ session.setAttribute(Const.CURRENT_USER,response.getData()); } return response;}} ...

February 21, 2023 · 5 min · jiezi

关于java:Java代码工具之中英文语句分词

在自然语言解决中比拟热门的操作就是中文或英文语句分词了,分词就是依照不同的算法和参数将语句分成若干词汇。拆分后的关键词能够进行词频统计或者词云图片生成等,可能疾速不便的找到语句的外围主题热点。在java开发中,如果单纯进行原始性能开发,分词性能耗时耗力,成果不肯定能达到现实后果。有一个比拟风行的代码工具平台“昂焱数据”,其官网网址为www.ayshuju.com。下面有封装好的各种性能代码工具。该网站上的“语句分词及类似度比照”java代码工具能够间接应用,中文语句分词反对的分词算法包含Lucene、Ansj、corenlp、HanLP、IKAnalyzer、Jcseg、Jieba、mmseg4j、MYNLP、Word等10种;英文语句分词反对的分词算法包含IKAnalysis、StanfordNlp等两种支流算法。上面将“语句分词及类似度比照”工具应用步骤做一下记录:第一步:下载并装置jar到本地maven库登录该网站,在“代码工具”一栏找到“语句分词及类似度比照”代码工具,代码工具如下图所示:下载该代码工具并解压,双击“”执行,将提醒的maven坐标粘贴到我的项目的pom文件中即可。第二步:将该jar包的maven坐标粘贴到我的项目的pom文件中第三步:残缺的测试代码如下 package com.example.demo.test;import com.angyan.tool.word.base.enums.ChineseTokenizerEnum;import com.angyan.tool.word.base.enums.EnglishTokenizerEnum;import com.angyan.tool.word.util.TokenizerUtil;import java.util.List;/** * @author angyankj */public class ParticipleTest { public static void main(String[] args) { // 中文文本 String chnContent = "昂焱数据是为IT行业各种角色人员提供丰盛的一站式技术资源的平台!"; // 中文分词 String chnResult = TokenizerUtil.getChineseTokenizerResult(ChineseTokenizerEnum.ANSJ, chnContent); // 打印中文分词后果 System.out.println(chnResult); // 英文文本 String engContent = "Love is not a maybe thing. You know when you love someone."; // 英文分词 List<String> engResult = TokenizerUtil.getEnglishTokenizerResult(EnglishTokenizerEnum.IKANALYZER, engContent); // 打印英文分词后果 System.out.println(engContent); }}中文分词及英文分词的运行后果如下(分词之间以空格隔开):

February 21, 2023 · 1 min · jiezi

关于java:Quartz-JDBCBased-JobStore

JDBC-Based JobStore指的是应用数据库长久化存储作业相干信息的JobStore,与之对应的是基于内存的RAMJobStore。 咱们后面学习Quartz的Job、JobDetail、Trigger、作业调度线程以及作业执行线程的时候,大部分状况下是基于RAMJobStore进行剖析的,所以对RAMJobStore曾经有了理解,然而还不太理解JDBC-Based JobStore。 咱们从以下几个角度剖析JDBC-Based JobStore: 波及到的表以及各表的作用作业调度线程以及作业执行线程的基于JDBC-Based JobStore的次要工作过程JDBC-Based JobStore的事务管理及锁机制JDBC-Based JobStore的recovery及Misfire解决以上4点放在一片文章中可能会太长,所以咱们可能会分2篇文章进行剖析,明天先剖析第1/2两个问题。 JDBC-Based JobStore波及到的表JDBC-Based JobStore波及到的表(省略前缀qrtz_): JOBDETAILS:作业信息TRIGGERS:Trigger信息SIMPLE_TRIGGERS:Simple Trigger信息CRON_TRIGGERS:Cron Trigger信息FIRED_TRIGGERS:被触发的TriggersSCHEDULER_STATE:调度服务的状态LOCKS:锁Job及Trigger的注册注册过程咱们在后面的文章中曾经说过了,RAMJobStore与JDBC-Based JobStore的区别次要是存储形式不同,一个存储在内存中,一个存储在数据库中。 Job注册后存储在JOBDETAILS表中,内容与存储在内存中基本一致,次要包含sched_name、name、group、description、job_class_name、job_data等。 Trigger注册后存储在TRIGGERS表中,内容与存储在内存中的也基本一致,次要包含sched_name、name、group、job_name、job_group、trigger_state、trigger_type、start_time、end_time、calendar_name、misfire_instr、job_data等。 trigger_state在初始化写入之后的状态为WAITING(期待被触发)。 trigger_type包含: SIMPLE:Simple TriggerCRON:Cron TriggerCAL_INT:Calendar Interval TriggerDAILY_I:Daily Time Interval TriggerBLOB:A general blob Trigger不论是jobdetails还是trigger表都有一个sched_name字段,记录以后的任务调度器名称,sched_name从配置文件中取值(org.quartz.scheduler.instanceName)。 这个调度器名称sched_name其实就是运行任务调度服务的服务器标识,也就是以后正在运行quartz的服务的标识,集群环境下,不同的服务必须指定不同的sched_name,无关quartz集群的细节咱们在其余文章做详细分析。 Trigger注册的时候还波及到其余表:如果以后Trigger处于Group Paused状态,Trigger同时写入paused_trigger_grps表。 Simple Trigger会写入Simple_triggers表,Cron Trigger会写入Cron_triggers表,负责记录各自的非凡属性;Simple_triggers记录repeat_count/repeat_interval/times_triggered等信息,Cron_triggers表记录cron表达式。 作业的调度调度工作的执行逻辑咱们在后面的文章中曾经重复剖析过: 从作业执行线程池获取availThreadCount,也就是以后可用的线程数调用JobStore的acquireNextTriggers办法,获取特定短时间(idleWaitTime,默认30秒)内可能须要被触发的,数量不超过availThreadCount的触发器调用JobStore的triggersFired办法对获取到的可能须要被触发的触发器进行二次加工,再次获取到最终的待触发的触发器后果集循环解决最终的待处理触发器后果集中的每一个须要被触发的触发器用JobRunShell包装该触发器做绑定的Job,送给线程池执行作业JobStoreSupport#acquireNextTriggersJobStoreSupport是JDBC-Based JobStore的抽象类,有两个实现类JobStoreCMT和JobStoreTX,两个实现类的次要作用是治理事务,大部分的业务逻辑其实都是在JobStoreSupport中实现的,包含acquireNextTriggers和triggersFired办法。 acquireNextTriggers办法首先从Triggers表中获取符合条件的触发器:nextFireTime在30秒内(有参数idleWaitTime设定)的、状态为WAITING的、肯定数量的(参数设置的一次解决的触发器个数、或者可用的执行线程数)的触发器。 针对获取到的每一个Trigger: 从JobDetails表中获取其绑定的Job,判断Job的ConcurrentExectionDisallowed属性,做并发管制(逻辑与RAMJobStore的相干逻辑一样)Triggers表中以后trigger的状态从WAITING批改为ACQUIRED以后Trigger写入到fired_triggers表中,状态为ACQUIREDJobStoreSupport#triggersFired再次从triggers表中获取到以后trigger,验证其状态是否为ACQUIRED,状态不正确的不做解决。 从job_details表中获取到以后trigger绑定的作业。 更新fired_triggers表中以后trigger的状态为EXECUTING。 调用trigger的triggered办法获取以后trigger的下一次触发工夫。 更新triggers表中以后trigger的状态(WAITING)、下次触发工夫等数据。 作业执行#JobRunShell通过以上步骤,作业调度线程就获取到了以后须要执行的trigger,之后就须要执行最初一步: 用JobRunShell包装该触发器,送给线程池执行该触发器关联的作业咱们须要对这个JobRunShell做一个简略理解。 JobRunShell instances are responsible for providing the 'safe' environment for Job s to run in, and for performing all of the work of executing the Job, catching ANY thrown exceptions, updating the Trigger with the Job's completion code, etc.A JobRunShell instance is created by a JobRunShellFactory on behalf of the QuartzSchedulerThread which then runs the shell in a thread from the configured ThreadPool when the scheduler determines that a Job has been triggered.JavaDoc说的十分分明,JobRunShell其实才是最终负责Job运行的,SimpleThreadPool只是提供了运行Job的线程,有工作交进来之后(交进来的其实是持有job对象的JobRunShell对象)SimpleThreadPool负责调配一个执行线程、之后用该线程运行该工作。 ...

February 21, 2023 · 1 min · jiezi

关于java:自定义枚举值校验器

前言定义一个枚举类,现须要对传入的枚举值进行范畴校验。 @Getterpublic enum UserTypeEnum { STUDENT(“1”, "学生"), TEACHER(“2”, "老师"); private Integer code; private String name; UserTypeEnum(Integer code, String name) { this.code = code; this.name = name; }}当超出范围时,报错。 注解校验@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD, ElementType.METHOD})@Constraint(validatedBy = EnumValidatorHandler.class)public @interface EnumValidator { Class<?> value(); String message() default "入参值不在正确枚举中"; String method() default "getCode"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {};}@Slf4jclass EnumValidatorHandler implements ConstraintValidator<EnumValidator, Object> { private List<Object> values = new ArrayList<>(); @Override public void initialize(EnumValidator enumValidator) { Class<?> clazz = enumValidator.value(); Object[] objects = clazz.getEnumConstants(); try { Method method = clazz.getMethod(enumValidator.method()); Object value; for (Object obj : objects) { value = method.invoke(obj); values.add(value); } } catch (Exception e) { log.error("解决枚举校验异样:{}", e); } } @Override public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { if (value instanceof String) { String valueStr = (String)value; return StringUtils.isEmpty(valueStr)|| values.contains(value); } return Objects.isNull(value) || values.contains(value); }}应用在实体类的属性上退出注解 ...

February 21, 2023 · 1 min · jiezi

关于java:使用终端工具给你的电脑发送弹窗提醒

大家好,我是良许。 当初人手一部智能手机,这些智能手机都有个十分实用的性能,那就是弹窗揭示。当咱们收到短信,或者微信信息时,手机就会弹窗显示信息的大抵内容。有了这个性能你就不会错过重要信息了。 电脑上也有相似的性能,也很实用。但这个性能都是零碎级别,咱们能不能通过脚本形式去调用这个弹窗性能呢? 答案是必定的! 例如,当脚本或 cron 工作实现时,长时间运行的编译工作失败,或者脚本执行过程中呈现紧急问题,这些状况下如果能在电脑上弹出一条揭示,必定会让隔壁的美女共事另眼相看! 以下代码已在 Linux 零碎上编写并测试通过,也能够移植到 Mac 电脑上。 从 Linux 终端发送弹窗告诉要从 Linux 终端发送告诉,须要应用 notify-send 命令。这个命令大部分发行版都没有默认装置,须要咱们自行入手。 在 Fedora 上,输出: $ sudo dnf install notify-send在基于 Debian 的发行版上,键入: $ sudo apt install notify-send几个简略弹窗告诉的例子: $ notify-send "liangxu is great!!"$ notify-send "welcome to liangxu's website" "www.lxlinux.net"这个命令不仅反对弹窗,还能够批改紧急水平、自定义图标等。更多信息能够通过 man notify-send 来查问。 你还能够在告诉注释中应用一小段 HTML 标记来为你的信息减少一些格局,比方:加粗、斜体,等等。最重要的是,URL 还反对点击,十分不便。例如: $ notify-send -u critical \ "Build failed!" \ "There were <b>123</b> errors. Click here to see the results: http://buildserver/latest" ...

February 21, 2023 · 3 min · jiezi

关于java:来一波骚操作Java内存模型

文章整顿自 博学谷狂野架构师什么是JMM 并发编程畛域的关键问题线程之间的通信线程的通信是指线程之间以何种机制来替换信息。在编程中,线程之间的通信机制有两种,共享内存和消息传递。 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信,典型的共享内存通信形式就是通过共享对象进行通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送音讯来显式进行通信,在java中典型的消息传递形式就是wait()和notify()。 线程间的同步同步是指程序用于管制不同线程之间操作产生绝对程序的机制。在共享内存并发模型里,同步是显式进行的。程序员必须显式指定某个办法或某段代码须要在线程之间互斥执行。 在消息传递的并发模型里,因为音讯的发送必须在音讯的接管之前,因而同步是隐式进行的。 古代计算机的内存模型物理计算机中的并发问题,物理时机到的并发问题与虚拟机中的状况有不少相似之处,物理机对并发的解决计划对于虚拟机的实现也有相当大的参考意义。 其中一个重要的复杂性起源是绝大多数的运算工作都不可能只靠处理器“计算”就能实现,处理器至多要与内存交互,如读取运算数据、存储运算后果等,这个I/O操作是很难打消的(无奈仅靠寄存器来实现所有运算工作)。 晚期计算机中cpu和内存的速度是差不多的,但在古代计算机中,cpu的指令速度远超内存的存取速度,因为计算机的存储设备与处理器的运算速度有几个数量级的差距,所以古代计算机系统都不得不退出一层读写速度尽可能靠近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲:将运算须要应用到的数据复制到缓存中,让运算能疾速进行,当运算完结后再从缓存同步回内存之中,这样处理器就毋庸期待迟缓的内存读写了。 基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,然而也为计算机系统带来更高的复杂度,因为它引入了一个新的问题:缓存一致性(Cache Coherence)。 在多处理器零碎中,每个处理器都有本人的高速缓存,而它们又共享同一主内存(MainMemory)。当多个处理器的运算工作都波及同一块主内存区域时,将可能导致各自的缓存数据不统一,举例说明变量在多个CPU之间的共享。 如果真的产生这种状况,那同步回到主内存时以谁的缓存数据为准呢?为了解决一致性的问题,须要各个处理器拜访缓存时都遵循一些协定,在读写时要依据协定来进行操作,这类协定有MSI、MESI(Illinois Protocol)、MOSI、Synapse、Firefly及Dragon Protocol等。 该内存模型带来的问题古代的处理器应用写缓冲区长期保留向内存写入的数据。写缓冲区能够保障指令流水线继续运行,它能够防止因为处理器停顿下来期待向内存写入数据而产生的提早。 同时,通过以批处理的形式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的屡次写,缩小对内存总线的占用。 尽管写缓冲区有这么多益处,但每个处理器上的写缓冲区,仅仅对它所在的处理器可见。这个个性会对内存操作的执行程序产生重要的影响:处理器对内存的读/写操作的执行程序,不肯定与内存理论产生的读/写操作程序统一! 处理器A和处理器B按程序的程序并行执行内存拜访,最终可能失去x=y=0的后果。处理器A和处理器B能够同时把共享变量写入本人的写缓冲区(A1,B1),而后从内存中读取另一个共享变量(A2,B2),最初才把本人写缓存区中保留的脏数据刷新到内存中(A3,B3)。 当以这种时序执行时,程序就能够失去x=y=0的后果。 从内存操作理论产生的程序来看,直到处理器A执行A3来刷新本人的写缓存区,写操作A1才算真正执行了。尽管处理器A执行内存操作的程序为:A1→A2,但内存操作理论产生的程序却是A2→A1。 Processor AProcessor B代码a=1; //A1 x=1; //A2b=2; //B1 y=a; //B2运行后果初始状态 a=b=0 处理器容许失去后果 x=y=0 Java内存模型定义JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作形式。JVM是整个计算机虚构模型,所以JMM是隶属于JVM的。 从形象的角度来看,JMM定义了线程和主内存之间的形象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个公有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的正本。 本地内存是JMM的一个抽象概念,并不实在存在。它涵盖了缓存、写缓冲区、寄存器以及其余的硬件和编译器优化。 Java内存区域 Java虚拟机在运行程序时会把其主动治理的内存划分为以上几个区域,每个区域都有的用处以及创立销毁的机会,其中蓝色局部代表的是所有线程共享的数据区域,而紫色局部代表的是每个线程的公有数据区域。办法区办法区属于线程共享的内存区域,又称Non-Heap(非堆),次要用于存储已被虚拟机加载的类信息、常量、动态变量、即时编译器编译后的代码等数据,依据Java 虚拟机标准的规定,当办法区无奈满足内存调配需要时,将抛出OutOfMemoryError 异样。 值得注意的是在办法区中存在一个叫运行时常量池(Runtime Constant Pool)的区域,它次要用于寄存编译器生成的各种字面量和符号援用,这些内容将在类加载后寄存到运行时常量池中,以便后续应用。 JVM堆Java 堆也是属于线程共享的内存区域,它在虚拟机启动时创立,是Java 虚拟机所治理的内存中最大的一块,次要用于寄存对象实例,简直所有的对象实例都在这里分配内存,留神Java 堆是垃圾收集器治理的次要区域,因而很多时候也被称做GC 堆,如果在堆中没有内存实现实例调配,并且堆也无奈再扩大时,将会抛出OutOfMemoryError 异样。 程序计数器属于线程公有的数据区域,是一小块内存空间,次要代表以后线程所执行的字节码行号指示器。字节码解释器工作时,通过扭转这个计数器的值来选取下一条须要执行的字节码指令,分支、循环、跳转、异样解决、线程复原等根底性能都须要依赖这个计数器来实现。 虚拟机栈属于线程公有的数据区域,与线程同时创立,总数与线程关联,代表Java办法执行的内存模型。每个办法执行时都会创立一个栈桢来存储办法的的变量表、操作数栈、动静链接办法、返回值、返回地址等信息。每个办法从调用直完结就对于一个栈桢在虚拟机栈中的入栈和出栈过程,如下(图有误,应该为栈桢): 本地办法栈本地办法栈属于线程公有的数据区域,这部分次要与虚拟机用到的 Native 办法相干,个别状况下,咱们无需关怀此区域。 小结这里之所以简要阐明这部分内容,留神是为了区别Java内存模型与Java内存区域的划分,毕竟这两种划分是属于不同档次的概念。 Java内存模型概述Java内存模型(即Java Memory Model,简称JMM)自身是一种形象的概念,并不实在存在,它形容的是一组规定或标准,通过这组标准定义了程序中各个变量(包含实例字段,动态字段和形成数组对象的元素)的拜访形式。 因为JVM运行程序的实体是线程,而每个线程创立时JVM都会为其创立一个工作内存(有些中央称为栈空间),用于存储线程公有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域, 所有线程都能够拜访,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的本人的工作内存空间,而后对变量进行操作,操作实现后再将变量写回主内存,不能间接操作主内存中的变量,工作内存中存储着主内存中的变量正本拷贝, 后面说过,工作内存是每个线程的公有数据区域,因而不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来实现,其简要拜访过程如下图 ...

February 21, 2023 · 1 min · jiezi

关于java:MyBatis-学习笔记Mapper扫描

Mapper 扫描须要依赖 Maybtis/Spring 这个我的项目,官网文档: https://mybatis.org/spring/zh/index.html Mapper 扫描依赖两种形式: 通过 @Mapper 注解 (想通过该注解实现扫描 Mapper ,须要依赖 mybatis/spring-boot-starter 这个我的项目)通过 @MapperScan 注解无论是 @Mapper 还是 @MapeprScan 注解,底层都是须要去注册一个 MapperScannerConfigurer 的 Bean , 而后通过该 Bean 来实现 Mapper 的被动注入。 @Mapper 注解@Mapper 注解次要应用在 Mapper 接口上,如果要实现只对 @Mapper 注解的接口实现扫描,则须要引入 mybatis/spring-boot-starter 这个我的项目。 mybatis/spring-boot-starter 应用办法请参考:https://github.com/mybatis/spring-boot-starter/blob/master/mybatis-spring-boot-autoconfigure/src/site/zh/markdown/index.md AutoConfiguredMapperScannerRegistrar 则是主动扫描 @Mapper 注解接口的实现类。 然而该类启用的条件,必须是 Spring 没有找到 MapperScannerConfigurer 和 MapperFactoryBean 的 bean 。 源码剖析AutoConfiguredMapperScannerRegistrar 是 MybatisAutoConfiguration 的动态外部类,故这里展现的是 MybatisAutoConfiguration 的源码。 /*** MybatisAutoConfiguration 是一个配置类 * 启用该配置类须要我的项目存在 SqlSessionFactory 和 SqlSessionFactoryBean 这两个类(这两个类都在 mybatis/spring 我的项目中)* mybatis/spring-boot-starter 会主动引入 mybatis/spring*/@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })@ConditionalOnSingleCandidate(DataSource.class)@EnableConfigurationProperties(MybatisProperties.class)@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })public class MybatisAutoConfiguration implements InitializingBean { // 疏忽局部源码,这里只展现要探讨的局部 /** * 对 @Mapper 注解主动扫描的实现类 * 它继承了 ImportBeanDefinitionRegistrar 接口,实现了 registerBeanDefinitions() 办法 * registerBeanDefinitions() 办法次要是创立并注册 MapperScannerConfigurer 的 BeanDefinition */ public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; private Environment environment; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 如果没有配置主动扫描的包门路,那么则终止扫描 Mapper if (!AutoConfigurationPackages.has(this.beanFactory)) { logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); return; } logger.debug("Searching for mappers annotated with @Mapper"); // 获取主动扫描的门路 List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (logger.isDebugEnabled()) { packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg)); } // 创立一个 MapperScannerConfigurer 的 BeanDefinitionBuilder // 并且设置 MapperScannerConfigurer 的字段,如 processPropertyPlaceHolders、annotationClass、basePackage 等字段 // 后续 MapperScannerConfigurer 就会通过这些字段信息去扫描包 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", true); // annotationClass 示意扫描的包中必须要有指定的注解,这里指定要有 Mapper 这个注解 builder.addPropertyValue("annotationClass", Mapper.class); builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName) .collect(Collectors.toSet()); if (propertyNames.contains("lazyInitialization")) { // Need to mybatis-spring 2.0.2+ builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"); } if (propertyNames.contains("defaultScope")) { // Need to mybatis-spring 2.0.6+ builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}"); } // 默认设置 sqlSessionTemplateBeanName 的值为 sqlSessionTemplate boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class, Boolean.TRUE); if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) { ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory; Optional<String> sqlSessionTemplateBeanName = Optional .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory)); Optional<String> sqlSessionFactoryBeanName = Optional .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory)); if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) { builder.addPropertyValue("sqlSessionTemplateBeanName", sqlSessionTemplateBeanName.orElse("sqlSessionTemplate")); } else { builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get()); } } builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册 MapperScannerConfigurer 的 BeanDefinition registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); } // 疏忽前面的代码 } /** * 该类应用了 @Configuration 注解,故在调用 AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions() 前,先通过这个类的逻辑: * 如果是曾经存在了 MapperFactoryBean 或者是 MapperScannerConfigurer 的 Bean * 那么就不会加载 AutoConfiguredMapperScannerRegistrar 的 Bean 了,因而也不会去创立 MapperScannerConfigurer 的 BeanDefinition,做后续的 Mapper 扫描了。 * 其中 MapperScannerConfigurer 的 Bean 能够通过应用 @MapperScan 注解来创立(故如果应用了 @MapperScan 注解,就不会主动扫描 @Mapper 注解的 Mapper) * 或者咱们自定义了一个 Bean ,返回的是 MapperFactoryBean ,那么也不会主动扫描 @Mapper 注解的 Mapper */ @org.springframework.context.annotation.Configuration(proxyBeanMethods = false) @Import(AutoConfiguredMapperScannerRegistrar.class) @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class }) public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { @Override public void afterPropertiesSet() { logger.debug( "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); } }}@MapperScan 注解@MapperScan 注解是 mybatis 主动扫描的罕用形式。 它通过应用了 @Import 注解,导入了 MapperScannerRegistrar 类,并且后续通过 MapperScannerRegistrar 来做 Mapper 扫描的逻辑。 MapperScan 的源码如下: ...

February 21, 2023 · 7 min · jiezi

关于java:Spring-BeanFactory-解析

SpringSpring容器,通常指的是ApplicationContext体系结构;即整个Spring框架的IOC性能,是通过ApplicationContext接口的实现类来为应用程序服务的,应用程序通过ApplicationContext的办法来间接与Bean工厂交互,如获取Bean对象实例等ApplicationContext的顶层接口正是beanFactory;因而,Spring容器的实质也能够了解为bean工厂(beanFactory,或者bean容器),它依照咱们的要求,生产须要的各种bean;在生产bean的过程中,为了解决bean之间的依赖问题,才引入了依赖注入(DI)这种技术;也就是说依赖注入是beanFactory生产bean时为了解决bean之间的依赖的一种技术而已BeanDefinitionprotected AbstractBeanDefinition(BeanDefinition original) { setParentName(original.getParentName()); setBeanClassName(original.getBeanClassName()); setScope(original.getScope()); setAbstract(original.isAbstract()); setFactoryBeanName(original.getFactoryBeanName()); setFactoryMethodName(original.getFactoryMethodName()); setRole(original.getRole()); setSource(original.getSource()); copyAttributesFrom(original); // 此处省略}从源码可知,BeanDefinition其实是Bean的一些元数据定义,包含父类名称(parenName)、类名(baenClassName)、作用域(scope)等信息; 通过BeanDefinitionReader获取到BeanDefinition之后,咱们再通过BeanDefinitionRegistry将beanDefinition注册到BeanFacory中,存储在BeanFactory的conCurrentHashMap中,key=beanName,Value=BeanDefinition; 那么获取Bean就从conCurrentHashMap中通过BeanName获取对应的Bean信息 BeanDefinitionReaderbean元数据读取器(或能够了解为解析器)BeanDefinitionReader会从 xml文件;@Component等注解类;@Configuration等配置类,获取BeanDefinition,而后注册到BeanFactory中;留神,AnnotatedBeanDefinitionReader并没有实现BeanDefinitionReader,此处的BeanDefinitionReader泛指元数据读取器 BeanFactory即bean工厂,所有开始的源头在我学习SpringBoot启动的过程中上下文默认加载的是DefaultListableBeanFactory,本文临时不进行深究,等前期有闲暇再来补完; DefaultListableBeanFactory是BeanFactory接口体系的默认实现类,提供BeanDefinition的存储map,Bean对象的存储map;而Bean对象实例的存储map,定义在FactoryBeanRegistrySupport,FactoryBeanRegistrySupport实现了SingletonBeanRegistry接口,而DefaultListableBeanFactory的基类AbstractBeanFactory,继承于FactoryBeanRegistrySupport BeanFactoryPostProcessorBeanFactoryPostProcessor是BeanFactory的后置处理器,BeanFactory创立实现,加载其所蕴含的所有获取BeanDefinition,但还没有实例化bean之前,执行,具体为调用postProcessBeanFactory办法加载更多的bean元数据ConfigurationClassPostProcessor,用于从BeanFactory中检测应用了@Configuration注解的类,对于这些类对应的BeanDefinitions汇合,遍历并顺次交给ConfigurationClassParser,ConfigurationClassBeanDefinitionReader解决,别离是解决与@Configuration同时应用的其余注解和将类外部的应用@Bean注解的办法,生成BeanDefinition,注册到BeanFactory对bean元数据进行加工解决BeanDefinition属性填充&批改,在postProcessBeanFactory办法中,能够对beanFactory所蕴含的beanDefinitions的propertyValues和构造函数参数值进行批改,如应用PropertyPlaceHolderConfigurer来对BeanDefinition的propertyValues的占位符进行填充与赋值,或者应用PropertyResourceConfigurer获取config文件中属性,对BeanDefinitions的相干属性进行赋值或者值笼罩BeanPostProcessorBeanPostProcessor是bean对象后置处理器,负责对已创立好的bean对象进行加工解决,次要是能够对新创建的bean实例进行批改,提供了一个相似于hook机制,对创立好的bean对象实例进行批改postProcessBeforeInitialization,创立完bean实例,在任何初始化办法执行之前,执行该办法postProcessAfterInitialization,创立完bean实例,在所有初始化办法执行之后,执行该办法(未完) 参考资料:https://blog.csdn.net/u010013...https://blog.csdn.net/qq_3668...https://blog.csdn.net/weixin_...

February 20, 2023 · 1 min · jiezi

关于java:如何判断线程池执行完毕

办法一,应用ExecutorService 和 Future在 Java 中,能够应用 ExecutorService 和 Future 接口来判断线程池中的工作是否执行结束。上面是一个简略的示例: ExecutorService executor = Executors.newFixedThreadPool(10); // 创立一个大小为10的线程池List<Future<?>> futures = new ArrayList<>(); // 用于保留提交到线程池中的工作// 提交工作到线程池中for (int i = 0; i < 100; i++) { Future<?> future = executor.submit(new Runnable() { @Override public void run() { // 执行工作 } }); futures.add(future);}// 期待所有工作实现for (Future<?> future : futures) { try { future.get(); // 期待工作执行结束 } catch (InterruptedException | ExecutionException e) { // 解决异样 }}// 敞开线程池executor.shutdown();在这个示例中,咱们首先创立了一个大小为10的线程池,而后提交了100个工作到线程池中,并应用 Future 接口保留这些工作的执行后果。最初,咱们应用一个循环来期待所有工作执行结束,通过调用 Future.get() 办法来期待每个工作的执行后果,如果工作执行过程中产生了异样,咱们能够在 catch 代码块中进行解决。最初,咱们调用 shutdown() 办法来敞开线程池。 ...

February 20, 2023 · 1 min · jiezi

关于java:VS-Code-Spring-全新功能来了

大家好,欢送来到咱们 2023 年的第一篇博客!咱们想与您分享几个与 Spring 插件、代码编辑和性能相干的激动人心的更新,让咱们开始吧! Spring 插件包的新入门演练演练(Walkthrough) 是一种多步骤、向导式的体验,用于传授用户如何应用具备丰盛内容的插件。自从 Visual Studio Code 引入插件的演练格局以来,它就广受欢迎。因为咱们对 Spring 插件做了很多改良,因而咱们对 Spring 插件包的现有演练进行了更新。 新的演练反映了咱们的最新性能,例如 bean 和 API 映射视图以及来自 Spring Boot Dashboard 的实时内存视图。它还蕴含更简洁直观的信息,以便新用户能够更流畅地理解如何应用 Spring 插件。这是新演练的演示。 新的演练包含四个步骤: ▌第 1 步 – 从 Spring Boot 我的项目开始在此步骤中,您能够应用 Spring Initializer 从头开始创立我的项目,也能够从 GitHub 克隆现有示例我的项目。 ▌第 2 步 – 摸索您的我的项目一旦你有了一个我的项目,插件中有不同的工具能够帮忙你疾速导航到 Spring 部件。Bean 和端点映射是 Spring Boot Dashboard UI 布局的一部分,您能够间接跳转到此步骤中的视图。 ▌第 3 步 – 运行您的我的项目此步骤阐明如何运行我的项目。要从咱们的插件中享受大多数最新的 Spring 相干性能,您能够单击 Spring Boot Dashboard 中的“播放”按钮。或者,您能够应用此步骤中形容的其余办法。 ▌第 4 步 – 查看正在运行的应用程序的实时信息咱们工具的一个独特性能是可能可视化正在运行的 Spring 应用程序的实时数据。查看实时数据,从 Spring Boot Dashboard 运行应用程序。您将可能看到 bean、端点映射以及新增加的实时内存信息。 ...

February 20, 2023 · 1 min · jiezi

关于java:偏向锁-10-连问被问懵圈了

前言对于Hotpot JVM中的偏差锁,大部分开发者都比拟相熟或者至多据说过。那咱们用上面10个对于偏差锁的进阶问题,测验一下本人离精通还有多远。 如何判断以后锁对象为偏差锁偏差锁如何判断锁重入当代码运行至synchronized润饰的代码块时,合乎什么条件才会尝试获取偏差锁线程进入偏差锁后,会不会创立lock record偏差锁收缩后,lock record有什么变动如何判断以后持有锁的线程曾经因为批量重偏差,而被撤销了偏差锁批量撤销和批量重偏差的触发条件是什么批量重偏差后,lock record和锁对象有什么变动批量撤销后,lock record和锁对象有什么变动批量撤销/重偏差后,新创建的锁对象,是否反对偏差锁看了下面的问题,如果是胸有成竹,那就能够跳过这篇文章了。如果一脸问号,这篇文章应该对你有所帮忙。 名词解释首先明确下文章中用到的名词,因为不同人可能叫法不一样。 对象头,Java对象在堆中存储时,会依照对象头加实例数据的构造来存储。这篇文章只讲锁,所以个别是指对象头中的Markword局部。 klass对象,jvm在加载类之后,会在堆内存中生成该类的对象,就是咱们代码中this.getClass()获取的对象。 锁对象, synchronized指定的锁对象。对于一般办法,这个对象默认是this指针。对于静态方法,锁对象是堆里的class对象。 Lock record,进入synchronized时在线程栈中生成的锁记录,对这个不相熟的能够百度一下或看一下《深刻java虚拟机》这本书 锁收缩,hotspot中从轻量级锁升级成重量级锁称之为收缩,为了便于了解,通常把偏差锁升级成轻量级锁也称为收缩。 问题解析问题1:如何判断以后锁对象为偏差锁 这个问题比较简单,个别理解过对象头或者偏差锁的都比拟相熟。当锁对象为偏差锁时,Markword的偏差锁标识位为1,锁标识位为01。即markword的最初3位为101。 问题2:偏差锁如何判断锁重入 接下面问题的Markword构造,当曾经有线程获取到偏差锁,它的id就会填到markword中的线程id中。重入时线程只有查看thread id里存的是否就是本人线程的id就能够了。 问题3:合乎什么条件才会尝试获取偏差锁 首先,hotspot中通过参数UseBiasedLocking管制是否启用偏差锁,不设置时默认是启用的。如果想要禁用偏差锁,能够在启动参数中增加-XX:-UseBiasedLocking。 是不是这样答复这个问题就完结了呢?答案是否定的。hotspot还有一个提早偏差的概念,就是在jvm启动的时候是有一个延迟时间,过了这段时间后偏差锁才开始启用。这个延迟时间通过启动参数BiasedLockingStartupDelay来设置,默认为4秒。那提早的目标是什么呢?hotspot的解释是在jvm启动过程中,外部有多个逻辑会用到锁,比方类加载。如果一开始就启用偏差锁,就导致频繁的撤销偏差锁,偏差锁的撤销须要在平安点执行,这样有可能影响jvm启动的速度。 满足下面2个条件之后,是不是就欢快的进入偏差锁了呢,其实还要通过2关。 第三个条件就是锁对象没有收缩,如果锁对象曾经收缩成轻量级锁了,那就不会再走偏差锁了。这就是常常说的锁只反对降级,不反对降级。轻量级锁的markword如下: 最初,如果锁对象对应的class产生了批量撤销的动作,也不会再进入偏差锁了。比方有10个锁对象lockobj0..lockobj9,他们都是LockObj类的实例,如果产生偏差锁的批量撤销,那在这10个锁对象上的抢锁操作都不会再走偏差锁逻辑。 问题4:线程进入偏差锁后,会不会创立lock record 理解轻量级锁逻辑的都晓得,轻量级锁加锁后,锁对象会保留lock record的援用,关系如下: 那偏差锁有没有呢?答案是有的。其实轻量级锁的这个lock record在运行至synchronized的时候就创立了,这个时候jvm还不晓得具体应用的是偏差锁还是轻量级锁,偏差锁和轻量级锁用的是同一个lock record。偏差锁的时候,对象头里没有lock record的指针。 然而,咱们再深挖一层,是不是每次都会创立?答案是否定的。比方在同一个办法中,对同一个锁对象的重入,就不会再次创立lock record,比方上面的代码(尽管不会有人这么写代码): public void testSync() { synchronized (this) { //first time synchronized (this) { // second time } } }问题5:偏差锁收缩后,lock record有什么变动 首先,来看下收缩前的lock record和锁对象,它们的关系如下: 栈中的lock record蕴含了指向锁对象的指针和markword的正本。锁收缩后可能呈现两种状况: 1)抢锁线程取得了轻量级锁,则替换lock record中的displace_header的锁状态位为无锁。 2)如果是轻量级锁的锁重入,则会降lock record的displace_header设置为空 ...

February 20, 2023 · 1 min · jiezi

关于java:Java-集合中的排序算法浅析

作者:京东物流 秦彪1.  引言排序是一个Java开发者,在日常开发过程中随处可见的开发内容,Java中有丰盛的API能够调用应用。在Java语言中,作为汇合工具类的排序办法,必然要做到通用、高效、实用这几点特色。应用什么样排序算法会比拟适合,可能做到在尽量升高工夫、空间复杂度的状况下,又要兼顾保障稳定性,达到优良的性能。可能从性能角度登程首先会想到的是疾速排序,或者归并排序。作为jdk提供的通用排序功能,应用又如此频繁,必定有独特之处,一起来看学习下期中的神秘。 文中不会过多的介绍几大根本排序算法的形式、由来和思维,次要精力集中在一块探讨java中排序办法所应用的算法,以及那些是值得咱们学习和借鉴的内容。文中如有了解和介绍的谬误,一起学习,一起探讨,一起提高。 2.  案例日常应用最为频繁的排序,莫过于如下代码案例,给定一个现有的序列进行肯定规定下的排序,配合java8的stream个性,能够很不便的对一个汇合进行排序操作(排序规定只是对排序对象及排序计划的限定,不在本文探讨范畴内)。 List<Integer> list = Arrays.asList(10, 50, 5, 14, 16, 80);System.out.println(list.stream().sorted().collect(Collectors.toList()));在代码执行的过程中SortedOps.java类中 Arrays.sort(array, 0, offset, comparator); 执行了Array汇合类型的sort排序算法。 @Overridepublic void end() { Arrays.sort(array, 0, offset, comparator); downstream.begin(offset); if (!cancellationWasRequested) { for (int i = 0; i < offset; i++) downstream.accept(array[i]); } else { for (int i = 0; i < offset && !downstream.cancellationRequested(); i++) downstream.accept(array[i]); } downstream.end(); array = null;}如果应用Collections.sort() 办法如下打印 list1 和 list2 后果一样,且调用的都是 Arrays 汇合类中的 sort 办法。 ...

February 20, 2023 · 6 min · jiezi

关于java:面试官限流算法有哪些

限流的实现算法有很多,但常见的限流算法有三种:计数器算法、漏桶算法和令牌桶算法。 1.计数器算法计数器算法是在肯定的工夫距离里,记录申请次数,当申请次数超过该工夫限度时,就把计数器清零,而后从新计算。当申请次数超过距离内的最大次数时,回绝拜访。 计数器算法的实现比较简单,但存在“突刺景象”。 突刺景象是指,比方限流 QPS(每秒查问率)为 100,算法的实现思路就是从第一个申请进来开始计时,在接下来的 1 秒内,每来一个申请,就把计数加 1,如果累加的数字达到了 100,后续的申请就会被全副回绝。等到 1 秒完结后,把计数复原成 0,从新开始计数。如果在单位工夫 1 秒内的前 10 毫秒解决了 100 个申请,那么前面的 990 毫秒会申请回绝所有的申请,咱们把这种景象称为“突刺景象”。 计数器算法的简略实现代码如下: import java.util.Calendar;import java.util.Date;import java.util.Random;public class CounterLimit { // 记录上次统计工夫 static Date lastDate = new Date(); // 初始化值 static int counter = 0; // 限流办法 static boolean countLimit() { // 获取以后工夫 Date now = new Date(); Calendar calendar = Calendar.getInstance(); calendar.setTime(now); // 以后分 int minute = calendar.get(Calendar.MINUTE); calendar.setTime(lastDate); int lastMinute = calendar.get(Calendar.MINUTE); if (minute != lastMinute) { lastDate = now; counter = 0; } ++counter; return counter >= 100; // 判断计数器是否大于每分钟限定的值。 } // 测试方法 public static void main(String[] args) { for (; ; ) { // 模仿一秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Random random = new Random(); int i = random.nextInt(3); // 模仿1秒内申请1次 if (i == 1) { if (countLimit()) { System.out.println("限流了" + counter); } else { System.out.println("没限流" + counter); } } else if (i == 2) { // 模仿1秒内申请2次 for (int j = 0; j < 2; j++) { if (countLimit()) { System.out.println("限流了" + counter); } else { System.out.println("没限流" + counter); } } } else { // 模仿1秒内申请10次 for (int j = 0; j < 10; j++) { if (countLimit()) { System.out.println("限流了" + counter); } else { System.out.println("没限流" + counter); } } } } }}2.漏桶算法漏桶算法的实现思路是,有一个固定容量的漏桶,水流(申请)能够依照任意速率先进入到漏桶里,但漏桶总是以固定的速率匀速流出,当流入量过大的时候(超过桶的容量),则多余水流(申请)间接溢出。如下图所示:漏桶算法提供了一种机制,通过它能够让突发流量被整形,以便为零碎提供稳固的申请,比方 Sentinel 中流量整形(匀速排队性能)就是此算法实现的,如下图所示:更多 Sentinel 内容详见:https://mp.weixin.qq.com/s/nF5f18BP8hscqIEmIFRN8Q ...

February 20, 2023 · 2 min · jiezi

关于java:DispatcherServlet-SpringMVC-启动过程

ServletContextServletContext是WEB利用的全局的贮存信息的空间,服务器启动就创立,服务器敞开则销毁,即利用上下文 Spring容器的初始化WEB利用启动时,ServletContext初始化,Spring提供的ContextLoaderListener会监听到这个事件,ContextLoaderListener.contextInitialized办法会被调用 // 在这个办法中,Spring会初始化根上下文,即WebApplicationContextinitWebApplicationContext(event.getServletContext()) WebApplicationContext理论的实现类是XmlWebApplicationContext,这个就是Spring的IOC容器,对应治理的Bean的定义由web.xml中的context-param标签指定(即applicationContext.xml)在这个IOC容器初始化结束后,Spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取 SpringMVC的启动过程web.xml中的配置 <servlet><servlet-name>service_dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/services/service_dispatcher-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>这段配置会初始化DispatcherServlet,load-on-startup示意启动容器时初始化该Servlet SpringMVC启动的过程,实际上是DispatcherServlet的初始化过程 DispatcherServlet 继承关系为 FrameworkServlet -> HttpServletBean -> HttpServlet -> Servlet,通过应用Servlet API 来对HTTP申请进行响应,成为SpringMVC的前端处理器,用以转发、匹配、解决每个申请。 DispatcherServlet初始化从HttpServletBean笼罩父类的init办法开始public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. try { // 取得web.xml中的contextConfigLocation配置属性(即springmvc-config.xml),Spring MVC的配置文件 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); // 获取ServletContext ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); // 模板办法,能够在子类中调用,做一些初始化工作,bw代表DispatcherServelt initBeanWrapper(bw); // 将配置的初始化值设置到DispatcherServlet中 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like. // 模板办法,子类初始化的入口办法 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); }}HttpServletBean中通过Spring的委托类BeanWrapper来对DispatcherServlet设值,并在FrameworkServlet.initServletBean()中进一步初始化上下文 ...

February 19, 2023 · 1 min · jiezi

关于java:SpringBoot-SpringBootApplication

SpringBootSpringBoot是Java利用的开发框架,基于Spring4.0设计,继承了Spring框架原有的优良个性,还并通过简化配置来进一步优化了Spring利用的整个搭建和开发过程,SpringBoot通过集成大量的框架,很好的解决了依赖包版本抵触,以及援用不稳定性等问题 SpringBoot 个性创立独立的Spring利用内嵌Tomcat或Jetty等Servlet容器,利用打包后即可间接运行提供一系列预设配置来简化配置尽可能主动配置Spring容器与三方类库提供可用于生产的个性,如度量、运行状况检查和内部化配置不须要代码生成,不须要XML配置主动拆卸主动拆卸是为了从spring.factories文件中获取到对应须要进行主动拆卸的类,并生成相应的Bean对象,而后将它们交给spring容器来治理嘛,主动拆卸是 SpringBoot 的外围,咱们只有引入一个 starter 组件依赖就能实现主动拆卸 依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.3</version> <relativePath/> <!-- lookup parent from repository --></parent>启动类 @SpringBootApplicationpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}@SpringBootApplication@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication { // 内容省略}由源码可知,@SpringBootApplication理论是由3个要害的注解组成 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan@SpringBootConfiguration@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration@Indexedpublic @interface SpringBootConfiguration { // 内容省略}由源码可知,@SpringBootConfiguration 实际上就是一个 @Configuration 注解,就是为了让以后类作为一个配置类交由 Spring 的 IOC 容器进行治理 @ComponentScan@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Repeatable(ComponentScans.class)public @interface ComponentScan { // 内容省略}由源码可知,@ComponentScan 等价于在 xml 文件中配置 context:component-scan,SpringBoot 会默认扫描以后类所在的包及其子包中的所有标注了 @Component,@Service,@Controller 等注解的类 ...

February 19, 2023 · 2 min · jiezi

关于java:SpringBoot-启动流程

启动入口@SpringBootApplicationpublic class DemoApplication { public static void main(String[] args) { // step.1 SpringApplication.run(DemoApplication.class, args); }}实例化 SpringApplication 对象// step.2public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args);}// step.3 // 实例化SpringApplication对象,在构造方法内加载一些资源public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}// step.4public SpringApplication(Class<?>... primarySources) { this(null, primarySources);}加载运行类信息 & 确定利用类型 & 加载初始化器 & 加载监听器 & 设置运行主类// step.5@SuppressWarnings({ "unchecked", "rawtypes" })public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 加载运行类信息,此处的primarySources实际上是启动传入的class this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 确定利用类型,默认servlet this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // 加载初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 加载监听器 setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class)); // 设置运行主类 this.mainApplicationClass = deduceMainApplicationClass();}如果心愿自定义初始化器,则须要实现 ApplicationContextInitializer 接口加载心愿自定义监听器,则须要实现 ApplicationListener 接口以上两者都须要在resources目录下增加 META-INF/spring.factories 配置文件,将自定义的初始化器注册进去 ...

February 19, 2023 · 2 min · jiezi

关于java:一个比-Redis-性能更强的数据库

给大家举荐一个比Redis性能更强的数据:KeyDB KeyDB是Redis的高性能分支,侧重于多线程、内存效率和高吞吐量。除了性能改良外,KeyDB还提供被动复制、闪存和子密钥过期等性能。KeyDB具备MVCC架构,容许您在不阻塞数据库和升高性能的状况下执行密钥和扫描等查问。 KeyDB与Redis协定、模块和脚本放弃齐全兼容。这包含对脚本和事务的原子性保障。因为KeyDB与Redis开发放弃同步,所以KeyDB是Redis性能的超集,使KeyDB成为现有Redis部署的代替产品。 在雷同的硬件上,KeyDB能够实现比Redis高得多的吞吐量。被动复制简化了热备盘故障切换,使您能够轻松地跨正本散发写入,并应用简略的基于TCP的负载平衡/故障切换。KeyDB的更高性能容许您在更少的硬件上做更多的事件,从而升高操作老本和复杂性。 上面的图表比拟了几种KeyDB和Redis设置,包含最新的Redis6 io-线程选项和TLS基准测试。 如果你的利用对性能十分刻薄,Redis曾经无奈满足,无妨试试这个更弱小的数据库吧! 我的项目地址:https://github.com/Snapchat/K...官网地址:https://docs.keydb.dev/ 欢送关注我的公众号:程序猿DD。第一工夫理解前沿行业音讯、分享深度技术干货、获取优质学习资源

February 19, 2023 · 1 min · jiezi

关于java:马上卸载这个恶心的软件

大家好,我是良许。 春节曾经过完了,但在这喜庆的日子里,又有一个小丑在上窜下跳了。 没错,这个不要脸的小丑仍然还是 Notepad++ 的作者。 好好的一个开发者,为何老喜爱整一些有得没得的货色?好好搬砖写代码不香吗?非要落得一身骂名? 这厮曾经不是第一次做这种令人不齿的破事了。 早在 08 年奥运会它就开始跳进去了,以及 20 年 HK 风波,22 年冬奥会,它都要进去无病呻吟一波。 在 19 年它为「XJ人权」问题「发声」时,我就立马卸载了这个 SB 人开发的软件,并且也在我的粉丝群号召大家不要再应用这个软件了。 不得不说,notepad++ 的确是一款十分优良的软件,玲珑、简洁、功能强大,在得悉这厮反华之前,我始终都是它的忠诚用户。 但软件是软件,zz 是 zz,开源社区是洁净且纯正的,容不得任何人去玷污! 也正是因为这种垃圾人的存在,才导致 GitHub 变得如此不好拜访! 真的是一颗老鼠屎毁了一锅粥! 「如果不批准政治观点,就在你的源码中增加随机字符」,好家伙,这是什么匪徒逻辑?本人吃 shit 也就算了,还要强制他人跟你一起吃 shit ? 你这也太看得起本人了吧? 你还真把本人当回事了吧? 除了 Notepad++ 难道就没有其它软件可用了吗? 这样的软件,有多远滚多远!当初电脑上还装置这款软件的敌人,请动动手指头,一起把它丢进历史的垃圾筒里! 放弃 notepad++ 后,我转而投向了 Sublime Text 的怀抱,它能完满满足我的需要,完满代替 Notepad++ ,一个字:真香! 实际上,还有很多软件能够完满代替 Notepad++ ,比方: VS Code Notepad3 Notepad--(之前公众号举荐过) …… 有这么多软件可用,为何非你不可?? 写这篇文章的时代,忽然发现它的官网打不开了,心愿它就此隐没吧。 学习编程,千万不要急于求成,肯定要多读一些经典书籍,多看源码,多下苦功夫去死磕代码,这样技术能力出息。给大家分享一些程序员必读经典书籍,肯定要多读几遍: 收费送给大家,只求大家金指给我点个赞! 程序员必读经典书单(高清PDF版) 有播种?心愿老铁们来个三连击,给更多的人看到这篇文章举荐浏览: 干货 | 程序员进阶架构师必备资源免费送刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!欢送关注我的博客:良许Linux教程网,满满都是干货! ...

February 19, 2023 · 1 min · jiezi

关于java:良许翻天覆地的2022年

大家好,我是良许,新年快乐呀~ 在我女室友坚定不移的致力之下,2022年的最初一天我终于被她传染了,阳了~ 此时的我,正顶着37多度的低烧写下这篇年终总结。 2022年,对于大多数人而言,封控是主旋律——不停地核酸,不停地居家。特地是对于做实体行业的小伙伴,真的是损失惨重。 我在广州租住的中央有个步行街,我就亲眼看着那边的店铺一家家新开,又一家家开张。甚至看到新开的店铺,我就跟女室友打赌,赌它什么时候开张。 然而,像我这种做自媒体的,封控对我来讲简直没任何影响。不就居家嘛,给我一台电脑,一根网线,我在月球都能够办公。 说没影响也是假的,大环境不好的状况下,谁都无奈独善其身。当初也齐全放开了,不管好坏,咱们都应该承受这个事实。 好了,不说这个主观的大环境了,跟大家分享我的2022年吧。我的2022年真的能够用天翻地覆来形容,当然有好的一面,也有不好的一面。 在这一年里,我从广州搬回了福州,在短视频和直播畛域扎根下来,并且也进军了常识付费。 1. 来到广州2017年7月初,我从厦门搬去了广州,2022年7月初,我又从广州回归福州,在广州呆了整整5年。 良许来到广州 很多人问我为什么来到广州,广州毕竟是一线城市,各方面的资源都比福州好,而且也有很多投资人想投资我做点小事件。 确实,在广州我也意识了很多大佬,见过很多公司老板、高管,也结交了很多跟我一样的创业者,并且也有跟身价几亿的投资人畅聊行业趋势。 但对于我来讲,在广州,总有点漂的感觉,没有真正安宁下来。尽管我也有能力在广州安家落户,但总是感觉这里不是我真正的家。 当初我回到福州了,尽管比不上广州,但这里的所有我都感觉十分亲切。而且当初住进了本人买的房子里,一切都是那么虚浮而安宁。 良许乔迁故居啦~ 尽管我回到了二线城市,但在广州的5年,我的集体能力也失去了飞速的晋升,也是我从一名一般打工者变质成创业者的中央。 所以,我仍然还是激励年轻人,肯定要去大城市,哪怕是闯荡几年也行,这会极大宽阔你的视线和眼界。 2. 公众号相熟我的敌人都晓得,我是做公众号起家的。从2018年6月到当初曾经四年半了,而且我也会保持写下去。 在公众号这个平台,我赚到了人生的第一个百万,也通过公众号我被很多人意识,同时也结交了很多大牛,所以我对公众号始终都十分有感情的。 然而随着短视频和直播的冲击,当初公众号的整体数据始终在稳步降落。 我在只有14W粉丝的时候,均匀浏览量上万。而当初,我曾经靠近22W粉丝,均匀浏览量也只有六七千。 没错,粉丝涨了,浏览量反而跌了。 当然,也可能因为我最近原创变少的起因(忙于做视频,精力有限)。 而且,公众号的行情也在变差。不晓得大家有没发现,这一年来很多公众号发的广告越来越少了,甚至不少号曾经卖身或停更? 说实话,无利不起早,当你赚不到钱的时候,你就没有更新的能源了。 别跟我讲情怀,那些说本人写公众号是因为情怀的,我没见哪一个保持下来的。 不瞒大家说,我从公众号赚到的钱,也一样缩小了好几成。但我不会放弃的,公众号就是我的后花园,粉丝就是我的老朋友,偶然跟老朋友聊聊天也是十分开心的事。 最近忙于录课,原创少了些,等我录完课我就多更新一些干货,大家等我哈~ 3. 短视频在两年前我就曾经摸索短视频,但因为办法形式不对,始终没做起来。 在年前年初的时候,我潜心钻研各种教程,钻研各种套路,精心打磨视频,往年我的短视频终于迎来了暴发! 我的重点是抖音,但各个短视频平台也在同步更新(视频号、快手、小红书、B站)。 前几个月我略微算了下,这几个平台一起涨粉了15W左右,而公众号涨到15W我用了近4年,可见短视频的威力所在。 当初我在抖音轻易发个视频,都有一两万的播放量,而同样的内容在公众号只有七八千。 所以,2022年我非常重视短视频这个平台,选题、写稿、拍摄、剪辑,每个环节都要花费大量的工夫,所以我也是我公众号原创降落的起因,毕竟精力有限嘛。 然而,尽管涨了这么多粉,我在短视频里基本上没赚到钱,这也是我用的办法不对吧。 但不管怎样,短视频是趋势,先把它做起来再说。过后我刚进军抖音的时候,我也不晓得怎么赚钱,但我晓得这是趋势。 4. 直播往年3月,我加入生财有术团队的抖音直播大航海,从那个时候开始直播,始终保持到当初,简直没有断播过。 我这人最大的长处就是——特地能保持。过后同期的1800多个船友,能保持直播到当初的,预计不到100人,甚至更少。 刚开始直播的时候,只有一两人在线,当初场观根本稳固在2~3W。在5月份的时候,在线人数甚至一度超过3300人,而且还被生财老板亦仁发现,并在外部进行宣传。 一开始我做直播也是没赚到钱的,但我还是仍然保持直播。还是那句话,我置信这是趋势。 除了本人保持直播,我每天还偷偷录制对标账号的直播,学习他们的内容、话术,而后利用在本人的直播间。 如果有常常来我直播间的小伙伴,应该有发现我的直播格调更换了好几款,这就是不停学习、不停迭代的后果。 将来,我还是会保持直播上来,无它,就是脸皮厚而已。 5. 常识付费刚开始做短视频和直播,始终都没有本人的变现产品,所以空有流量也愁于转化。 在5月份时,我花了一个半月的工夫,录制了一套Linux命令的课程,介绍了150个常用命令的用法。 这套课程内容很干,录制尽管花了一个半月,但后期的筹备也花了很久。这也是我做常识付费的一个摸索。 原本我想卖68元,但通过小考察,发现大家广泛感觉命令用到再百度即可。算了,我也懒得多说什么了,就卖9.9元得了,交个敌人。 学习Linux命令的正确姿态 回到福州之后,我见了同在福州的抖音号主「上官社长」,跟他聊了一下午,交谈甚欢,于是一拍即合,一起联手做嵌入式培训! 于是第二天,我就搬进他的公司办公,就是这么干脆利落! 上官老师尽管只比我大一岁,但他的教训、经历、眼界,都远高于我。尽管咱们是单干关系,但我始终尊他为良师。 做职业教育,必定离不开被说割韭菜。然而,为常识、为服务而付费在当初这个时代是再失常不过的事件,我心愿大家能辩证对待这个问题。 收了钱,不做事,那是纯纯的割韭菜;然而收了钱,我把你造就成一名嵌入式工程师,这是如许双赢的一件事? 涨粉5.5W,良许又开始搞事了……. 6. 小小的总结很多人说2022年很操蛋,然而对于一个创业者而言,没有经验过一些波澜,怎么对得起「守业」这个头衔? 2022年过来了,很多人拍手叫好,但说实话,我十分不舍!尽管2022年有很多黑天鹅事件,而且口罩事件让大家叫苦连天,但—— 这才是人生! 2022年尽管所有都处于低迷,但一样的N多人发大财(国难财除外)。说因为疫情而没有赚到钱的,那只是你的借口罢了。 ...

February 19, 2023 · 1 min · jiezi

关于java:微信-支付流程

概述之前写了支付宝领取流程相干的思考,其实与微信侧的相似,2边只是领取具体实现略有不同;实质上对于商户端的设计应该都是通用,此处不再赘述;后续有工夫再来欠缺本文,如有趣味可点击上面链接 支付宝 领取流程异样解决 设计思考 官网文档https://pay.weixin.qq.com/wik... 时序图

February 19, 2023 · 1 min · jiezi

关于java:马上卸载这个恶心的软件

大家好,我是良许。 春节曾经过完了,但在这喜庆的日子里,又有一个小丑在上窜下跳了。 没错,这个不要脸的小丑仍然还是 Notepad++ 的作者。 好好的一个开发者,为何老喜爱整一些有得没得的货色?好好搬砖写代码不香吗?非要落得一身骂名? 这厮曾经不是第一次做这种令人不齿的破事了。 早在 08 年奥运会它就开始跳进去了,以及 20 年 HK 风波,22 年冬奥会,它都要进去无病呻吟一波。 在 19 年它为「XJ人权」问题「发声」时,我就立马卸载了这个 SB 人开发的软件,并且也在我的粉丝群号召大家不要再应用这个软件了。 不得不说,notepad++ 的确是一款十分优良的软件,玲珑、简洁、功能强大,在得悉这厮反华之前,我始终都是它的忠诚用户。 但软件是软件,zz 是 zz,开源社区是洁净且纯正的,容不得任何人去玷污! 也正是因为这种垃圾人的存在,才导致 GitHub 变得如此不好拜访! 真的是一颗老鼠屎毁了一锅粥! 「如果不批准政治观点,就在你的源码中增加随机字符」,好家伙,这是什么匪徒逻辑?本人吃 shit 也就算了,还要强制他人跟你一起吃 shit ? 你这也太看得起本人了吧? 你还真把本人当回事了吧? 除了 Notepad++ 难道就没有其它软件可用了吗? 这样的软件,有多远滚多远!当初电脑上还装置这款软件的敌人,请动动手指头,一起把它丢进历史的垃圾筒里! 放弃 notepad++ 后,我转而投向了 Sublime Text 的怀抱,它能完满满足我的需要,完满代替 Notepad++ ,一个字:真香! 实际上,还有很多软件能够完满代替 Notepad++ ,比方: VS Code Notepad3 Notepad--(之前公众号举荐过) …… 有这么多软件可用,为何非你不可?? 写这篇文章的时代,忽然发现它的官网打不开了,心愿它就此隐没吧。 学习编程,千万不要急于求成,肯定要多读一些经典书籍,多看源码,多下苦功夫去死磕代码,这样技术能力出息。给大家分享一些程序员必读经典书籍,肯定要多读几遍: 收费送给大家,只求大家金指给我点个赞! 程序员必读经典书单(高清PDF版) 有播种?心愿老铁们来个三连击,给更多的人看到这篇文章举荐浏览: 干货 | 程序员进阶架构师必备资源免费送刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!欢送关注我的博客:良许Linux教程网,满满都是干货! ...

February 19, 2023 · 1 min · jiezi

关于java:良许翻天覆地的2022年

大家好,我是良许,新年快乐呀~ 在我女室友坚定不移的致力之下,2022年的最初一天我终于被她传染了,阳了~ 此时的我,正顶着37多度的低烧写下这篇年终总结。 2022年,对于大多数人而言,封控是主旋律——不停地核酸,不停地居家。特地是对于做实体行业的小伙伴,真的是损失惨重。 我在广州租住的中央有个步行街,我就亲眼看着那边的店铺一家家新开,又一家家开张。甚至看到新开的店铺,我就跟女室友打赌,赌它什么时候开张。 然而,像我这种做自媒体的,封控对我来讲简直没任何影响。不就居家嘛,给我一台电脑,一根网线,我在月球都能够办公。 说没影响也是假的,大环境不好的状况下,谁都无奈独善其身。当初也齐全放开了,不管好坏,咱们都应该承受这个事实。 好了,不说这个主观的大环境了,跟大家分享我的2022年吧。我的2022年真的能够用天翻地覆来形容,当然有好的一面,也有不好的一面。 在这一年里,我从广州搬回了福州,在短视频和直播畛域扎根下来,并且也进军了常识付费。 1. 来到广州2017年7月初,我从厦门搬去了广州,2022年7月初,我又从广州回归福州,在广州呆了整整5年。 良许来到广州 很多人问我为什么来到广州,广州毕竟是一线城市,各方面的资源都比福州好,而且也有很多投资人想投资我做点小事件。 确实,在广州我也意识了很多大佬,见过很多公司老板、高管,也结交了很多跟我一样的创业者,并且也有跟身价几亿的投资人畅聊行业趋势。 但对于我来讲,在广州,总有点漂的感觉,没有真正安宁下来。尽管我也有能力在广州安家落户,但总是感觉这里不是我真正的家。 当初我回到福州了,尽管比不上广州,但这里的所有我都感觉十分亲切。而且当初住进了本人买的房子里,一切都是那么虚浮而安宁。 良许乔迁故居啦~ 尽管我回到了二线城市,但在广州的5年,我的集体能力也失去了飞速的晋升,也是我从一名一般打工者变质成创业者的中央。 所以,我仍然还是激励年轻人,肯定要去大城市,哪怕是闯荡几年也行,这会极大宽阔你的视线和眼界。 2. 公众号相熟我的敌人都晓得,我是做公众号起家的。从2018年6月到当初曾经四年半了,而且我也会保持写下去。 在公众号这个平台,我赚到了人生的第一个百万,也通过公众号我被很多人意识,同时也结交了很多大牛,所以我对公众号始终都十分有感情的。 然而随着短视频和直播的冲击,当初公众号的整体数据始终在稳步降落。 我在只有14W粉丝的时候,均匀浏览量上万。而当初,我曾经靠近22W粉丝,均匀浏览量也只有六七千。 没错,粉丝涨了,浏览量反而跌了。 当然,也可能因为我最近原创变少的起因(忙于做视频,精力有限)。 而且,公众号的行情也在变差。不晓得大家有没发现,这一年来很多公众号发的广告越来越少了,甚至不少号曾经卖身或停更? 说实话,无利不起早,当你赚不到钱的时候,你就没有更新的能源了。 别跟我讲情怀,那些说本人写公众号是因为情怀的,我没见哪一个保持下来的。 不瞒大家说,我从公众号赚到的钱,也一样缩小了好几成。但我不会放弃的,公众号就是我的后花园,粉丝就是我的老朋友,偶然跟老朋友聊聊天也是十分开心的事。 最近忙于录课,原创少了些,等我录完课我就多更新一些干货,大家等我哈~ 3. 短视频在两年前我就曾经摸索短视频,但因为办法形式不对,始终没做起来。 在年前年初的时候,我潜心钻研各种教程,钻研各种套路,精心打磨视频,往年我的短视频终于迎来了暴发! 我的重点是抖音,但各个短视频平台也在同步更新(视频号、快手、小红书、B站)。 前几个月我略微算了下,这几个平台一起涨粉了15W左右,而公众号涨到15W我用了近4年,可见短视频的威力所在。 当初我在抖音轻易发个视频,都有一两万的播放量,而同样的内容在公众号只有七八千。 所以,2022年我非常重视短视频这个平台,选题、写稿、拍摄、剪辑,每个环节都要花费大量的工夫,所以我也是我公众号原创降落的起因,毕竟精力有限嘛。 然而,尽管涨了这么多粉,我在短视频里基本上没赚到钱,这也是我用的办法不对吧。 但不管怎样,短视频是趋势,先把它做起来再说。过后我刚进军抖音的时候,我也不晓得怎么赚钱,但我晓得这是趋势。 4. 直播往年3月,我加入生财有术团队的抖音直播大航海,从那个时候开始直播,始终保持到当初,简直没有断播过。 我这人最大的长处就是——特地能保持。过后同期的1800多个船友,能保持直播到当初的,预计不到100人,甚至更少。 刚开始直播的时候,只有一两人在线,当初场观根本稳固在2~3W。在5月份的时候,在线人数甚至一度超过3300人,而且还被生财老板亦仁发现,并在外部进行宣传。 一开始我做直播也是没赚到钱的,但我还是仍然保持直播。还是那句话,我置信这是趋势。 除了本人保持直播,我每天还偷偷录制对标账号的直播,学习他们的内容、话术,而后利用在本人的直播间。 如果有常常来我直播间的小伙伴,应该有发现我的直播格调更换了好几款,这就是不停学习、不停迭代的后果。 将来,我还是会保持直播上来,无它,就是脸皮厚而已。 5. 常识付费刚开始做短视频和直播,始终都没有本人的变现产品,所以空有流量也愁于转化。 在5月份时,我花了一个半月的工夫,录制了一套Linux命令的课程,介绍了150个常用命令的用法。 这套课程内容很干,录制尽管花了一个半月,但后期的筹备也花了很久。这也是我做常识付费的一个摸索。 原本我想卖68元,但通过小考察,发现大家广泛感觉命令用到再百度即可。算了,我也懒得多说什么了,就卖9.9元得了,交个敌人。 学习Linux命令的正确姿态 回到福州之后,我见了同在福州的抖音号主「上官社长」,跟他聊了一下午,交谈甚欢,于是一拍即合,一起联手做嵌入式培训! 于是第二天,我就搬进他的公司办公,就是这么干脆利落! 上官老师尽管只比我大一岁,但他的教训、经历、眼界,都远高于我。尽管咱们是单干关系,但我始终尊他为良师。 做职业教育,必定离不开被说割韭菜。然而,为常识、为服务而付费在当初这个时代是再失常不过的事件,我心愿大家能辩证对待这个问题。 收了钱,不做事,那是纯纯的割韭菜;然而收了钱,我把你造就成一名嵌入式工程师,这是如许双赢的一件事? 涨粉5.5W,良许又开始搞事了……. 6. 小小的总结很多人说2022年很操蛋,然而对于一个创业者而言,没有经验过一些波澜,怎么对得起「守业」这个头衔? 2022年过来了,很多人拍手叫好,但说实话,我十分不舍!尽管2022年有很多黑天鹅事件,而且口罩事件让大家叫苦连天,但—— 这才是人生! ...

February 19, 2023 · 1 min · jiezi

关于java:类加载器-Classloader-双亲委派模型

Classloader在理解了类的生命周期与加载过程之后,接下来就须要理解类加载器,即Classloader。 JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其余类加载器全副继承自java.lang.ClassLoader BootstrapClassLoader(启动类加载器)最顶层的加载类,由 C++实现,负责加载 %JAVA_HOME%/lib目录下的 jar 包和类或者被 -Xbootclasspath参数指定的门路中的所有类ExtensionClassLoader(扩大类加载器)次要负责加载 %JRE_HOME%/lib/ext 目录下的 jar 包和类,或被 java.ext.dirs 零碎变量所指定的门路下的 jar 包。AppClassLoader(应用程序类加载器)面向咱们用户的加载器,负责加载以后利用 classpath 下的所有 jar 包和类。双亲委派模型当一个类加载器收到类加载工作时,会先交给本人的父加载器去实现,因而最终加载工作都会传递到最顶层的BootstrapClassLoader,只有当父加载器无奈实现加载工作时,才会尝试本人来加载。然而相熟Java的人都晓得,Java中只有父类而没有母类的说法,顾双亲其实具备误导性,自己更违心了解为,父类委派模型,以下为官网阐明 The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a “parent” class loader. When loading a class, a class loader first “delegates” the search for the class to its parent class loader before attempting to find the class itself.Classloader 加载过程protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{ synchronized (getClassLoadingLock(name)) { // 首先查看这个类是否曾经被加载,在JVM生命周期中,一个类只会加载一次 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { // 如果父类加载器不为空,则应用父类加载器加载 c = parent.loadClass(name, false); } else { // 如果父类加载器为空,则应用顶层bootstrap加载器 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 如果以上加载失败,则持续往下执行 } if (c == null) { // 走完上述代码仍没有加载胜利,则尝试本人加载 long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}双亲委派模型的益处保障了 Java 程序的稳固运行,能够防止类的反复加载,VM通过类名与加载器确定惟一的类保障了 Java 外围 API 不被篡改,如果没有应用双亲委派模型,而是每个类加载器加载各自的类,就会呈现一些问题,比方开发者本人定义一个名为 java.lang.Object 类,程序运行时,零碎就会呈现多个不同的 Object 类。如果不想用双亲委派模型如果不想突破双亲委派模型,就重写 ClassLoader 类中的 findClass() 办法即可,无奈被父类加载器加载的类最终会通过这个办法被加载如果想突破双亲委派模型则须要重写 loadClass() 办法自定义类加载器自定义加载器,须要继承 ClassLoader ...

February 18, 2023 · 1 min · jiezi

关于java:支付宝-支付流程异常处理-设计思考

概述本文次要形容对于领取流程相干的思考,并不进行代码编写阐明与领导,仅供流程设计参考下文将以商户APP唤起支付宝领取流程为例进行思路阐明支付宝API官网文档以上图流程为例 在商户APP中确认订单信息抉择领取形式,抉择支付宝唤起支付宝APP,输出领取明码调用支付宝SDK推送领取数据,展现支付宝APP的领取后果返回商户APP,展现最终领取后果时序图 领取流程可能呈现的异常情况与相应弥补伎俩 异样case解决预案商户服务端创单失败服务端外部进行重试,超过最大次数则须要返回零碎异样,告知用户,并告警&人工染指排查唤起支付宝创单并领取失败,买家信息异样&交易信息篡改上报风控,思考是否存在欺骗&黑产状况唤起支付宝创单并领取失败,买家状态非法&参数有效告警&人工染指排查唤起支付宝创单并领取失败,订单已领取or订单已敞开将此后果传给商户服务端进行信息确认,若确认已领取则进行失常业务流程,若已敞开须要思考同步最新的订单状态商户APP端,调用服务端接口推送领取后果超时服务端接管到支付宝回调前,须要思考轮循机制,定期查问支付宝API订单状态,确认是否领取胜利,超过肯定工夫没有后果时须要进入异样流程商户APP端,调用服务端接口推送领取胜利,服务端未监听到后果,或轮循支付宝接口为未领取上报风控,思考是否存在欺骗&黑产状况商户服务端确认领取胜利,更新订单状态失败,无奈执行后续流程调用支付宝退款接口,敞开订单,告诉用户 参考资料:https://blog.csdn.net/u014799...

February 18, 2023 · 1 min · jiezi

关于java:Spring-事务传播机制

简述Spring事务在源码中已定义得十分清晰,请间接浏览源码,联合本身业务场景应用,此处不过多赘述 @Transactional@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface Transactional { // 其余部分已省去 Propagation propagation() default Propagation.REQUIRED;}Propagationpublic enum Propagation {// 默认级别 若以后已有事务则退出 没有则新起事务REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),// 若以后已有事务则退出 没有则以非事务的形式运行SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),// 若以后已有事务则退出 没有则抛出异样MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),// 若以后已有事务则挂起 并创立新的事务REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),// 若以后已有事务则挂起 并以非事务的形式运行NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),// 若以后已有事务则报错 若无则以非事务的形式运行NEVER(TransactionDefinition.PROPAGATION_NEVER),// 若以后已有事务创立一个新事务并嵌套执行NESTED(TransactionDefinition.PROPAGATION_NESTED);}

February 18, 2023 · 1 min · jiezi

关于java:进程-线程-多线程-串行流-并行流

过程一个独立的正在执行的程序 线程一个过程的最根本的执行单位若将过程类比为社会中独立运行的实体,比方学校、医院、工厂,那么线程则能够了解为在实体中执行各自事务的人,比方老师,学生,医生,工人 多线程在同一个过程(即应用程序)中同时执行多个线程,进步了CPU的使用率须要留神 一个CPU,在同一个工夫点,只能有一个线程在执行多线程不能提高效率,反而会升高效率,然而能够进步CPU的使用率(线程来回切换,CPU则始终工作,没有休息时间)一个过程如果有多条执行门路,则称为多线程程序Java虚拟机的启动至多开启了两条线程,主线程和垃圾回收线程线程能够了解为过程的子工作串行流 & 并行流在日常开发中,常常会须要应用并行处理的场景来缩短执行工夫(对于计算机程序整体性能来说其实并没有进步,线程之间切换也是有老本的,然而对于工夫感触来说,执行工夫会变短,对于人的感知来说是进步了效率),JDK8的并行流就是一种简略无效的多线程解决模式,示例如下 // 串行流public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); list.stream().forEach(i -> { System.out.println(Thread.currentThread().getId() + " process: " + i); });}// 输入后果1 process: 11 process: 21 process: 31 process: 41 process: 51 process: 61 process: 71 process: 81 process: 91 process: 10// 并行流public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); list.parallelStream().forEach(i -> { System.out.println(Thread.currentThread().getId() + " process: " + i); });}// 输入后果20 process: 1015 process: 917 process: 220 process: 118 process: 515 process: 419 process: 816 process: 61 process: 714 process: 3

February 18, 2023 · 1 min · jiezi

关于java:类的-生命周期-加载过程

生命周期类从被加载到虚拟机内存中开始,到卸载出内存为止,是它的整个生命周期,其中包含 加载 Loading链接 Linking (验证 Verification,筹备 Preparation,解析 Resolution)初始化 Initialization应用 Using卸载 Unloading 7个阶段阶段程序加载、校验、筹备、初始化、卸载 这五个阶段的程序是确定的,然而【解析】则不肯定,在某些状况下能够在初始化之后才开始,这样做是为了反对 java 的运行时绑定特色(也称为前期绑定) 何时加载启动main办法时创立类的实例创立子类的实例拜访类的静态方法反射 Class.forName()加载机会知识点加载通过类的全限定名获取该类的二进制字节流;将二进制字节流所代表的动态构造转化为办法区的运行时数据结构;在内存中创立一个代表该类的java.lang.Class对象,作为办法区这个类的各种数据的拜访入口 链接验证验证阶段确保Class文件的字节流中蕴含的信息合乎以后虚拟机的要求,并且不会危害虚拟机本身的平安,包含 文件格式验证;验证字节流是否合乎 Class 文件格式的标准,并且能被以后版本的虚拟机解决是否以魔数 "0XCAFEBABE" 结尾;主次版本号是否在以后虚拟机解决范畴内;常量池是否有不被反对的常量类型;指向常量的索引值是否指向了不存在的常量;CONSTANT_Utf8_info 型的常量是否有不合乎UTF8编码的数据元数据验证;对字节码形容信息进行语义剖析,确保其合乎 Java 语法标准字节码验证;本阶段是验证过程中最简单的一个阶段,是对办法体进行语义剖析,保障办法在运行时不会呈现危害虚拟机的事件符号援用验证;本阶段产生在解析阶段,确保解析失常执行筹备筹备阶段是正式为类变量(或称 动态成员变量 )分配内存并设置初始值的阶段。这些变量(不包含实例变量)所应用的内存都在办法区中进行调配。初始值通常状况下是数据类型的零值(0, null),假如一个类变量的定义为: public static int value = 123;变量value在筹备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java办法。如果类字段的字段属性表中存在ConstantValue属性,那么在筹备阶段value就会被初始化为 ConstantValue 属性所指定的值,假如下面类变量 value 的定义变为: public static final int value = 123;那么在筹备阶段虚构机会依据ConstantValue的设置将value赋值为123。 解析解析阶段是虚拟机将常量池内的符号援用替换为间接援用的过程。符号解析知识点 初始化类初始化阶段是类加载过程的最初一步,是执行类结构器 <clinit>() 办法的过程。<clinit>() 办法是由编译器主动收集类中的所有类变量的赋值动作和动态语句块(static {} 块)中的语句合并产生的,编译器收集的程序是由语句在源文件中呈现的程序所决定的。动态语句块中只能拜访定义在动态语句块之前的变量,定义在它之后的变量,在后面的动态语句块中能够赋值,但不能拜访 public class Fentity {{ System.out.println("this is father");}public Fentity() {System.out.println("father is created");}public void test(){System.out.println("father test");}}public class Centity extends Fentity {{ System.out.println("this is child");}public Centity() {System.out.println("child is created");}public void test() {System.out.println("child test");}}public static void main(String[] args) { Fentity c = new Centity(); c.test();}输入后果father is createdthis is childchild is createdchild test参考资料:https://blog.csdn.net/weixin_...https://blog.csdn.net/chenshm...https://blog.csdn.net/xing_ji... ...

February 18, 2023 · 1 min · jiezi