关于java-ee:千峰JavaEE分布式开发2022全新升级交游落落俱星散

download:千峰JavaEE+分布式开发2022全新降级算法:计算机世界的魔法 关键字:算法、计算机、程序设计、复杂度、优化 在古代计算机科学中,算法是一项十分重要的技能。它们通过指定特定问题的解决方案,从而使计算机可能无效地执行各种工作。简略来说,算法就是计算机程序的“魔法”。 一般来说,算法能够被视为一组有序的操作步骤,这些步骤能够帮忙计算机解决问题。例如,在搜索引擎中,咱们应用算法来确定哪些网页最合乎咱们的查问条件。在游戏中,咱们应用算法来决定计算机角色如何口头。在电子商务中,咱们应用算法来建设举荐零碎。 只管算法曾经成为计算机领域不可或缺的工具,然而设计和实现一个高效的算法并不容易。在大多数状况下,一个好的算法须要思考工夫和空间的复杂度,以及如何优化算法以便在不同的平台上运行。 因为计算机速度的疾速晋升,许多看起来很慢的算法也变得更加实用。然而,这些算法的理论体现取决于计算机的硬件和软件环境。因而,为了确保算法的效率和可靠性,程序员必须常常进行优化。 只管算法的概念在计算机科学中曾经存在了很长时间,然而随着技术的倒退,人们对算法的需要也越来越多。例如,在人工智能和机器学习畛域,算法被用于构建简单的模型和预测将来趋势。 总的来说,算法是计算机世界的魔法。它们使咱们可能解决各种问题并发明出弱小的计算机程序。然而,设计和实现一个好的算法须要深刻了解计算机科学基础知识,并且须要一直地进行优化以确保其高效性和可靠性。

May 2, 2023 · 1 min · jiezi

关于java-ee:Flowable-已经执行完毕的流程去哪找

@[toc]在之前的文章中松哥和小伙伴们聊过,正在执行的流程信息是保留在以 ACT_RU_ 为前缀的表中,执行结束的流程信息则保留在以 ACT_HI_ 为前缀的表中,也就是流程历史信息表,当然这个历史信息表持续细分的话,还有好多种,明天咱们就来聊一聊这个话题。 假如我有如下一个流程: 当这个流程执行结束后,以 ACT_RU_ 为前缀的表中的数据均已清空,当初如果想查看刚刚执行过的流程信息,咱们就得去以 ACT_HI_ 为前缀的表中。 1. 历史流程信息历史流程信息查看,形式如下: @Testvoid test05() { List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().finished().list(); for (HistoricProcessInstance hpi : list) { logger.info("name:{},startTime:{},endTime:{}",hpi.getName(),hpi.getStartTime(),hpi.getEndTime()); }}调用的时候执行的 finished() 办法示意查问曾经执行结束的流程信息(从这里也能够看出,对于未执行结束的流程信息也会保留在历史表中)。 咱们来看下这个查问对应的 SQL,如下: SELECT RES.* , DEF.KEY_ as PROC_DEF_KEY_, DEF.NAME_ as PROC_DEF_NAME_, DEF.VERSION_ as PROC_DEF_VERSION_, DEF.DEPLOYMENT_ID_ as DEPLOYMENT_ID_ from ACT_HI_PROCINST RES left outer join ACT_RE_PROCDEF DEF on RES.PROC_DEF_ID_ = DEF.ID_ WHERE RES.END_TIME_ is not NULL order by RES.ID_ asc从这个 SQL 中能够看到,这个查问实质上就是查问的 ACT_HI_PROCINST 表。如下图: ...

November 8, 2022 · 3 min · jiezi

关于java-ee:小马哥的-Java-项目实战营完结

download:小马哥的 Java 我的项目实战营【完结】下崽ZY:https://www.zxit666.com/4223/匿名函数(lambda表达式)在Python中,函数能够算的上是“一等公民”了,咱们先回顾下函数的长处: 缩小代码反复量模块化代码 然而咱们有没有想过,如果咱们须要一个函数,比拟简短,而且只须要应用一次(无需反复调用),那还须要定义一个有名字的函数么?答案是否定的,这里咱们就能够应用匿名函数来实现这样的性能。咱们先看看求一个数的平方,咱们定义个函数怎么写:def square(x): return x**2square(3)复制代码而lambda表达式就能够这样写:square = lambda x: x**2square(3)复制代码依据下面的例子,其实lambda表达式应用还是很简略的,如下:lambda argument1, argument2,.....: expression复制代码接下来,介绍的map、filter和reduce函数,与lambda表达式联合应用,能力施展其弱小的作用了。map函数map函数的应用如下:map(function, iterable)复制代码其作用是,对iterable的每个元素,都使用function这个函数,最初返回新的可遍历的汇合。a = [1,2,3,4,5]b = map(lambda x: x*2,a)print(list(b)) [2, 4, 6, 8, 10]复制代码filter函数filter函数的应用如下:filter(function, iterable)复制代码其作用是,对iterable的每个元素,都使用function这个函数进行判断,最初返回全副为True的新的可遍历的汇合。a = [1,2,3,4,5,6]b = filter(lambda x :x%2 ==0, a)print(list(b)) [2, 4, 6]复制代码reduce函数reduce函数的应用如下:reduce(function, iterable)复制代码function规定有两个参数,示意对iterable每个元素和上一次运算的后果,进行function运算,最初失去一个值,这里要留神,咱们须要从functools中导入reduce。from functools import reduce a = [1,2,3,4]b = reduce(lambda x,y: x*y,a)print(b) 24 123*4复制代码总结 lambda表达式map、filter和reduce函数

April 2, 2022 · 1 min · jiezi

关于java-ee:JavaEE在线就业班20最新升级版GAGA

download:JavaEE在线待业班2.0【最新升级版】JavaScript 使用HTML 中的脚本必须位于 标签之间。 脚本可被搁置在 HTML 页面的 和 部分中。 会告诉 JavaScript 在何处开始和结束。之间的代码行蕴含了 JavaScript: 1 阅读器会解释并执行位于 之间的 JavaScript。 那些老旧的实例可能会在 13 14 15 您只能在 HTML 输入流中使用 document.write。 16 如果您在文档已加载后使用它(比如在函数中),会覆盖整个文档。 17 1 <!DOCTYPE html> 2 <html> 3 <body> 4 5 <p> 6 JavaScript 可能间接写入 HTML 输入流中: 7 </p> 8 9 <script>10 document.write("<h1>This is a heading</h1>");11 document.write("<p>This is a paragraph.</p>");12 </script>13 14 <p>15 您只能在 HTML 输入流中应用 document.write。16 如果您在文档已加载后应用它(比方在函数中),会笼罩整个文档。17 </p>18 19 </body>20 </html> ...

December 9, 2021 · 1 min · jiezi

关于java-ee:新版javaee18期开课吧2021完结无密

download:新版javaee18期【开课吧2021完结无密】后盾代码都是利用的 1.【get形式】使用jquery的get json与后盾交互 前端js代码片段 var data= {'a': $('input[name="a"]').val(),'b': $('input[name="b"]').val()}$.getJSON($SCRIPT_ROOT + '/_add_numbers',data, function(data) {$('#result').text(data.result);$('input[name=a]').focus().select();});后端pthon代码如下 ajax,Get形式与js交互(非表单)采纳了flask框架@app.route('/_add_numbers')def add_numbers():"""Add two numbers server side, ridiculous but well...""" a = request.args.get('a', 0, type=int) b = request.args.get('b', 0, type=int) log.info(a) log.info(b) return jsonify(result=a + b)2.【万能形式】使用jquery的ajax与后盾交互,设置不同的参数,可能get也可能post 下面的例子用ajax形式,前端代码如下 var data= { 'a': $('input[name="a"]').val(), 'b': $('input[name="b"]').val() }{# $.getJSON($SCRIPT_ROOT + '/_add_numbers',data, function(data) {#}{# $('#result').text(data.result);#}{# $('input[name=a]').focus().select();#}{# });#} $.ajax({ type: 'get', url: $SCRIPT_ROOT + '/_add_numbers', data: data, contentType: 'application/json; charset=UTF-8', dataType: 'json', success: function(data) { $('#result').text(data.result); $('input[name=a]').focus().select(); }, error: function(xhr, type,xxx) { alert('error ') } });后盾代码不便依然是 ...

December 5, 2021 · 1 min · jiezi

关于java-ee:黑马V11JavaEE精英进阶课

download:黑马V11|JavaEE精英进阶课創立測試數據接下來我們在 MySQL 中創立 RUNOOB 數據庫,並創立 websites 數據表,表構造如下: CREATE TABLE websites ( id int(11) NOT NULL AUTO_INCREMENT, name char(20) NOT NULL DEFAULT '' COMMENT '站點稱號', url varchar(255) NOT NULL DEFAULT '', alexa int(11) NOT NULL DEFAULT '0' COMMENT 'Alexa 排名', country char(10) NOT NULL DEFAULT '' COMMENT '國度', PRIMARY KEY (id) ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;插入一些數據: INSERT INTO websites VALUES ('1', 'Google', 'https://www.google.cm/', '1', 'USA'), ('2', '淘寶', 'https://www.taobao.com/', '13', 'CN'), ('3', '菜鳥教程', 'http://www.runoob.com', '5892', ''), ('4', '微博', 'http://weibo.com/', '20', 'CN'), ('5', 'Facebook', 'https://www.facebook.com/', '3', 'USA');package com.runoob.test; import java.sql.*; public class MySQLDemo { // MySQL 8.0 以下版本 - JDBC 驅動名及數據庫 URL static final String JDBC_DRIVER = "com.mysql.jdbc.Driver"; static final String DB_URL = "jdbc:mysql://localhost:3306/RUNOOB"; // MySQL 8.0 以上版本 - JDBC 驅動名及數據庫 URL //static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; //static final String DB_URL = "jdbc:mysql://localhost:3306/RUNOOB?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; // 數據庫的用戶名與明码,需要依據自己的設置 static final String USER = "root"; static final String PASS = "123456"; public static void main(String[] args) { Connection conn = null; Statement stmt = null; try{ // 注册 JDBC 驅動 Class.forName(JDBC_DRIVER); // 翻開链接 System.out.println("连接數據庫..."); conn = DriverManager.getConnection(DB_URL,USER,PASS); // 執行查问 System.out.println(" 實例化Statement對象..."); stmt = conn.createStatement(); String sql; sql = "SELECT id, name, url FROM websites"; ResultSet rs = stmt.executeQuery(sql); // 展開結果集數據庫 while(rs.next()){ // 經過字段檢索 int id = rs.getInt("id"); String name = rs.getString("name"); String url = rs.getString("url"); // 輸出數據 System.out.print("ID: " + id); System.out.print(", 站點稱號: " + name); System.out.print(", 站點 URL: " + url); System.out.print("\n"); } // 实现後關閉 rs.close(); stmt.close(); conn.close(); }catch(SQLException se){ // 處置 JDBC 錯誤 se.printStackTrace(); }catch(Exception e){ // 處置 Class.forName 錯誤 e.printStackTrace(); }finally{ // 關閉資源 try{ if(stmt!=null) stmt.close(); }catch(SQLException se2){ }// 什麼都不做 try{ if(conn!=null) conn.close(); }catch(SQLException se){ se.printStackTrace(); } } System.out.println("Goodbye!"); } } ...

November 26, 2021 · 2 min · jiezi

关于java-ee:JavaEE-tomcat10踩坑记录

1. 工具介绍tomcat: stable 10.0.6IDEA 2021.1.1maven: stable 3.8.12. 过程形容应用idea间接创立我的项目选用java enterprise我的项目主动生成pom文件, 引入servlet-api的依赖 <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope>编写我的项目没有问题, 运行我的项目当前动态资源没问题,servlet报错 HTTP状态 500 - 外部服务器谬误 ------ **类型** 异样报告 **音讯** 实例化Servlet类[com.xrluo.ee.HelloServlet]异样 **形容** 服务器遇到一个意外的状况,阻止它实现申请。 **例外情况** jakarta.servlet.ServletException: 实例化Servlet类[com.xrluo.ee.HelloServlet]异样 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:543) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:353) org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:870) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1696) org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) java.lang.Thread.run(Thread.java:748) **根本原因。** java.lang.NoClassDefFoundError: javax/servlet/http/HttpServlet java.lang.ClassLoader.defineClass1(Native Method) java.lang.ClassLoader.defineClass(ClassLoader.java:763) java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2470) org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:866) org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1370) org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1224) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:543) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:353) org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:870) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1696) org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) java.lang.Thread.run(Thread.java:748) **根本原因。** java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1401) org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1224) java.lang.ClassLoader.defineClass1(Native Method) java.lang.ClassLoader.defineClass(ClassLoader.java:763) java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2470) org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:866) org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1370) org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1224) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:543) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:353) org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:870) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1696) org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) java.lang.Thread.run(Thread.java:748)3. 解决问题目前迷茫中,实践来说maven会下载我的项目所须要的依赖, 为什么还会与tomcat外面的httpServlet有关系?? ...

May 28, 2021 · 1 min · jiezi

关于java-ee:数据库-001MySQL梳理系列一

MySQL根本组成 SQL执行流程 Server 层次要包含连接器、查问缓存、分析器、优化器、执行器,蕴含了MySQL次要的很多外围性能,以及所有的内置函数、存储过程、触发器、视图等,其实就是所有跨存储引擎的性能都是在这一层实现的存储引擎层,次要负责数据的存储和读取,是以插件的模式存在的 ,反对如 InnoDB 、MyISAM、Memory 等多个存储引擎,当初默认为 InnoDB查问缓存当一个SQL执行时首先会进入查问缓存查看之前是否执行过该语句,如果执行过则会以key-value的模式保留在缓存中,key是查问语句,value是查问后果如果缓存命中则间接返回后果,如果查问语句不在缓存中持续前面的流程大多数状况下咱们不举荐应用查问缓存,因为缓存生效十分频繁,只有一个更新,那么这个表上所有的缓存都会生效,吐过数据的更新比拟多,那么缓冲命中的效率很低,一直的在生效在MySQL中提供了参数 query_cache_type 参数来设置,默认是 DEMAND ,示意对默认的SQL都不应用查问缓存,如果要对特的语句进行缓存查问,则能够应用 SQL_CACHE 来显示的指定,如 select SQL_CACHE * from T where ID=1;在MySQL8.0 开始,查问缓存整个功能模块曾经删除掉不再领有分析器分析器次要蕴含词法剖析与语法分析词法剖析次要剖析一条SQL中各个字符串代表什么,比方 select 标记进去,这就是一个查问,在具体的表名,查问 的字段等等全副剖析进去语法分析次要是剖析SQL语句是否合乎MySQL的标准,如果咱们SQL写的有问题,那么常常看到的一个异样就是 You have an error in your SQL syntax 的提醒优化器对咱们的SQL进行优化,失去更高的执行打算如有多个索引时确定要用那个索引当有多变联查join 时,查问表的程序对查问条件和语句的优化执行器首先校验是否有对这张表的拜访权限,如果没有权限则会报错如果有,则依据引擎接口打开表进行数据的查问筛选Buffer Pool默认大小128MB, 偏小对于16核32G机器,能够调配2G内存,配置文件:my.ini 配置:[server]innodb_buffer_pool_size = 2147483648数据页MySQL中对数据进行形象,依照数据页的模式来寄存到文件,当查问时,首先定位到要查问数据所在的数据页,之后将整个数据页加载到Buffer Pool 中,数据页默认的大小是 16KB , 也就是一页数据蕴含16KB的数据在BufferPool 中的数据页个别咱们叫缓存页,默认状况下缓存页与磁盘上的数据页大小是对应的对于每个缓存页都有一个形容信息形容信息包含:数据页的所属表空间,数据页的标号,这个缓存页在Buffer Pool 中的内存地址以及其余一些信息在 Buffer Pool 中,所有的形容信息都在最后面,而后各个缓存页放在前面 形容数据大小相当于缓存页大小的5% 左右,也就是大略800字节,所以当咱们设置buffer pool 的大小为128MB ,然而实际上 Buffer Pool 的实在大小会超出一些,可能有有130MB 左右,这多进去的就是每个缓存页的形容信息表空间平时咱们创立张表时都会在磁盘上对应着一个表名.ibd , 这样的磁盘数据文件,这就是表空间的概念和物理体现对于一些零碎表空间可能存在着对应多个磁盘文件,咱们本人创立的表对应的表空间个别都是对应一个 表名.ibd 的数据文件数据区在表空间中有太多的数据页不好治理,这是引入了数据区的概念,英文:extent一个数据区中有间断的64个数据页,每个数据页16kb, 所以每个数据区大小是1MB同时265个数据区被划分为一组在表空间中第一组数据区的第一个数据区的前三个数据页是固定的,寄存一些非凡的描述性的信息 FSP_HDR 数据页: 寄存一些表空间和这一组数据区的属性IBUF——BITMAP 数据页:寄存这组数据页所有的 insert buffer 的一些信息INODE 数据页:寄存一些非凡信息表空间中其余各组数据区的第一个数据区的头两个数据页都是寄存非凡信息的本文由AnonyStar 公布,可转载但需申明原文出处。 欢送关注微信公账号 :云栖简码 获取更多优质文章 更多文章关注笔者博客 :云栖简码 i-code.online

January 26, 2021 · 1 min · jiezi

关于java-ee:效率工具-快速创建虚拟机Vagrant真香

Vagrant 是一个基于Ruby的工具,次要用于创立和部署虚拟化开发环境。它以来于Oracle的开源VirtualBox虚拟化零碎,通过应用 Chef创立自动化虚拟环境。Vagrant 次要的性能如下: 建设和删除虚拟机配置虚拟机相干参数治理虚拟机运行状态主动配置和装置开发环境打包和散发虚拟机运行环境因为 Vagrant 依赖于某种虚拟化技术,目前反对常见的 VirtualBox、 VMWare等,所以在应用Vagrant之前咱们须要先装置VirtualBox或 VMWare,不然无奈应用。举荐装置 VirtualBox。vagrant 能够疾速,不便,全自动的构建虚拟化环境,这也是咱们抉择它的起因,而不是让咱们像以前一样全副本人来部署。它相似与 docker 这种,有本人的仓库,咱们间接能够通过命令从仓库中拉取虚构镜像来疾速构建下载安装VirtualBox下载地址:https://www.virtualbox.org/wiki/Downloads ,下载好后装置间接下一步操作vagrant下载地址:https://www.vagrantup.com/downloads.html ,也是间接下一步的操作实现,须要重启电脑装置完。留神: 两者软件最好都下载最新的,省得呈现兼容问题,须要装置虚拟机,须要先开启处理器虚拟化技术,_VT-x/AMD-V硬件加速。_Vagrant根本命令命令作用vagrant box add增加box的操作vagrant init初始化box的操作,会生成vagrant的配置文件Vagrantfilevagrant up启动本地环境vagrant ssh通过 ssh 登录本地环境所在虚拟机vagrant halt敞开本地环境vagrant suspend暂停本地环境vagrant resume复原本地环境vagrant reload批改了 Vagrantfile 后,使之失效(相当于先 halt,再 up)vagrant destroy彻底移除本地环境vagrant box list显示以后曾经增加的box列表vagrant box remove删除相应的boxvagrant package打包命令,能够把以后的运行的虚拟机环境进行打包vagrant plugin用于装置卸载插件vagrant status获取以后虚拟机的状态vagrant global-status显示以后用户Vagrant的所有环境状态装置一个虚拟机案例首先咱们新建一个文件夹名字 vagrant ,这个名字随机,就是寄存要新建的虚拟机的配置的目录,之后在vagrant 目录中关上 cmd或Power Shell 窗口,执行上面命令:vagrant init centos/7 --box-version 2004.01PS D:\vagrant> vagrant init centos/7 --box-version 2004.01A `Vagrantfile` has been placed in this directory. You are nowready to `vagrant up` your first virtual environment! Please readthe comments in the Vagrantfile as well as documentation on`vagrantup.com` for more information on using Vagrant.下面命令执行完结后,在之下上面 up 命令,这个过程会去下载咱们须要的镜像,是比拟漫长的过程,下载完后会间接启动,vagrant up 命令原本就是启动命令,这是是因为没有所以会先去下载,PS D:\vagrant> vagrant upBringing machine 'default' up with 'virtualbox' provider...==> default: Box 'centos/7' could not be found. Attempting to find and install... default: Box Provider: virtualbox default: Box Version: 2004.01==> default: Loading metadata for box 'centos/7' default: URL: https://vagrantcloud.com/centos/7==> default: Adding box 'centos/7' (v2004.01) for provider: virtualbox default: Downloading: https://vagrantcloud.com/centos/boxes/7/versions/2004.01/providers/virtualbox.boxDownload redirected to host: cloud.centos.orgProgress: 3% (Rate: 371k/s, Estimated time remaining: 0:18:28)当然咱们也能够间接提前将镜像文件下载好,间接应用 vagrant box add {name} {url} 的命令进行本地装置,其中,{name} 是咱们要装置的名称, url 是咱们下载到本地的镜像门路PS D:\vagrant> vagrant box add centos/7 E:\迅雷下载\CentOS-7-x86_64-Vagrant-1905_01.VirtualBox.box==> box: Box file was not detected as metadata. Adding it directly...==> box: Adding box 'centos/7' (v0) for provider: box: Unpacking necessary files from: file:///E:/%D1%B8%C0%D7%CF%C2%D4%D8/CentOS-7-x86_64-Vagrant-1905_01.VirtualBox.box box:==> box: Successfully added box 'centos/7' (v0) for 'virtualbox'!如果是应用本地增加的,那么这里通过 vagrant up 来启动,如下:PS D:\vagrant> vagrant upBringing machine 'default' up with 'virtualbox' provider...==> default: Checking if box 'centos/7' version '2004.01' is up to date...==> default: Clearing any previously set forwarded ports...==> default: Clearing any previously set network interfaces...==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat==> default: Forwarding ports... default: 22 (guest) => 2222 (host) (adapter 1)==> default: Booting VM...==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222 default: SSH username: vagrant default: SSH auth method: private key==> default: Machine booted and ready!==> default: Checking for guest additions in VM... default: No guest additions were detected on the base box for this VM! Guest default: additions are required for forwarded ports, shared folders, host only default: networking, and more. If SSH fails on this machine, please install default: the guest additions and repackage the box to continue. default: default: This is not an error message; everything may continue to work properly, default: in which case you may ignore this message.==> default: Rsyncing folder: /cygdrive/d/vagrant/ => /vagrant==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`==> default: flag to force provisioning. Provisioners marked to run always will still run.启动后咱们能够通过 vagrant ssh 开启SSH,并登陆到 centos7 ...

January 6, 2021 · 3 min · jiezi

关于java-ee:数据库000-????Sysbench-数据库压力测试工具

000 - ????Sysbench 数据库压力测试工具 sysbench 是一个开源的、模块化的、跨平台的多线程性能测试工具,能够用来进行CPU、内存、磁盘I/O、线程、数据库的性能测试。目前反对的数据库有MySQL、Oracle和PostgreSQL。以下操作都将以反对MySQL数据库为例进行。1. Linux 上装置 sysbench 工具装置形式有两种,一种是通过 yum/apt 等来装置,另一种本人下载源码包来装置,笔者这里采纳的是centos ,采纳yum 装置sysbench 的源码包下载地址: http://sourceforge.net/projects/sysbench ,采纳源码装置的可转这里通过 yum 装置如下: 设置 yum repo 仓库curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash装置sudo yum -y install sysbench装置完查看是否胜利sysbench --version 2. 数据库测试用例筹备咱们这里测试的是 MySQL ,首先咱们在数据库创立一个专门用来测试的库 test_db 通过sysbench 创立20个测试表,每个表中创立100万数据,再通过10个线程对测试库发动拜访,继续5分钟,对其进行压测3. sysbench 构建表与数据执行上面命令筹备数据sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=192.168.56.10 --mysql-port=3306 --mysql-user=root --mysql-password=root --mysql-db=test_db --tables=20 --table_size=1000000 oltp_read_write --db-ps-mode=disable prepare下面命令的参数阐明: --db-driver=mysql : 示意数据库的驱动类型,咱们应用的是 MySQL 所以填mysql ,如果应用 Oracle 则填写相应的oracle--time=300 : 这个参数示意继续拜访的工夫 300秒--threads=10 : 示意应用10个线程模仿并发拜访--report-interval=1 : 示意每隔一秒输入以此压测状况 --mysql-host=192.168.56.10 --mysql-port=3306 --mysql-user=root --mysql-password=root : 这一块的配置,就是根本的数据库链接信息,指定数据库IP ,端口,账号密码--mysql-db=test_db --tables=20 --table_size=1000000 : 这三个参数设置,示意指定测试的库为test_db , 同时在这个库中构建20个表,并且每个表中构建出 100万条测试数据,表的名字会相似 sbtest1,sbtest2 这种格局oltp_read_write : 示意执行oltp 数据库的读写测试--db-ps-mode=disable : 禁止 ps 模式prepare : 示意依照命令设置去构建出咱们的数据,也就是对后面所有命令的执行计划4. 全方位测试1. 综合读写测试测试数据库的综合读写TPS ,应用 oltp_read_write 模式sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=192.168.56.10 --mysql-port=3306 --mysql-user=root --mysql-password=root --mysql-db=test_db --tables=20 --table_size=1000000 oltp_read_write --db-ps-mode=disable run留神:命令最初不再是 之前的 prepare,这里是 run ,示意运行压测,后面的是筹备数据成果 ...

December 30, 2020 · 2 min · jiezi

关于java-ee:JVM工具1-堆栈检查利器jstat的使用

jstat能够查看 JVM 整体的运行状况,能够看到 新生代,老年代等的内存应用状况,以及GC 次数和耗时命令格局 如 jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]其中 -option 必选参数表示命令参数 如 gc 等, -t 为可选参数示意是否打印工夫(秒),-h<lines> 可选参数,示意每隔多少行打印头部列表,如设置 -h 5 , 那么每五条记录就会从新打印表头,vmid 必选参数,Java 过程id,interval 可选参数示意采样的工夫距离,count 可选参数示意须要采样多少条, jstat -gc pidjstat -gc pid 这是最罕用的语法,能够间接查看内存和垃圾回收状况首先咱们要取得Java 过程的 PID 信息,能够通过 jps 命令来获取之后执行 jstat -gc pid 即可看到对应 Java 过程的内存状况,如下: 这里阐明一下对于这些列名的含意: S0C : 新生代中第一个 Survivor (即 From 区)的容量大小 (千字节)S1C : 新生代中第二个 Survivor (即 To 区)的容量大小 (千字节)S0U : 新生代 From Survivor 区已应用内存大小(千字节)S1U : 新生代 To Survivor 区一应用内存大小(千字节)EC : 新生代 Eden 区的容量大小(千字节)EU : 新生代 Eden 区已应用内存大小(千字节)OC : 老年代空间容量大小(千字节)OU : 老年代已应用内存大小(千字节)MC : 办法区的空间大小 (千字节)MU : 办法区已应用的空间大小(千字节)YGC : 从系统启动到当初 Young GC/Minor GC 的次数YGCT : Young GC 总耗时(秒)FGC :从系统启动到当初 Full GC 的次数FGCT : Full GC 的总耗时(秒)GCT : 垃圾回收总的耗时(秒)NGCMN :年老代( young )中初始化(最小)的大小 (千字节)         NGCMX :年老代( young )的最大容量 (千字节)         GC :年老代( young )中以后的容量 (千字节)         OGCMN : old 代中初始化(最小)的大小 (千字节)         OGCMX : old 代的最大容量 (千字节)         OGC : old 代以后新生成的容量 (千字节)         PGCMN : perm 代中初始化(最小)的大小 (千字节)         PGCMX : perm 代的最大容量 (千字节)           PGC : perm 代以后新生成的容量 (千字节)         S0 :年老代中 From survivor (幸存区)已应用的占以后容量百分比         S1 :年老代中 To Survivor (幸存区)已应用的占以后容量百分比         E :年老代中 Eden (伊甸园)已应用的占以后容量百分比         O : old 代已应用的占以后容量百分比         P : perm 代已应用的占以后容量百分比         S0CMX :年老代中 From survivor (幸存区)的最大容量 (千字节)         S1CMX :年老代中 To Survivor (幸存区)的最大容量 (千字节)         ECMX :年老代中 Eden (伊甸园)的最大容量 (千字节)         DSS :以后须要 survivor (幸存区)的容量 (千字节)( Eden 区已满)         TT : 持有次数限度         MTT : 最大持有次数限度 ...

December 18, 2020 · 2 min · jiezi

关于java-ee:顺序栈与链式栈的图解与实现

# 程序栈与链式栈的图解与实现 栈是一种非凡的线性表,它与线性表的区别体现在增删操作上栈的特点是先进后出,后进先出,也就是说栈的数据操作只能产生在末端,而不容许在两头节点进行操作 如上图所示,对栈的增删操作都只能在末端也就是栈顶操作,栈既然是线性表那么就存在表头和表尾,不过在栈构造中,对其都进行限度革新,表尾用来输出数据也叫做栈顶(top),相应的 表头就是栈底(bottom),栈顶和栈顶是两个指针用来示意这个栈与线性表相似,栈也是又程序示意和链式示意,别离称作程序栈和链栈栈的基本操作如何通过栈这个后进先出的线性表,来实现增删查呢?初始时,栈内没有数据,即空栈。此时栈顶就是栈底。当存入数据时,最先放入的数据会进入栈底。接着退出的数据都会放入到栈顶的地位。如果要删除数据,也只能通过拜访栈顶的数据并删除。对于栈的新增操作,通常也叫作 push 或压栈。对于栈的删除操作,通常也叫作 pop或出栈。对于压栈和出栈,咱们别离基于程序栈和链栈来剖析程序栈程序栈即就是顺序存储元素的,通常程序栈咱们能够通过数组来实现,将数组的首元素放在栈底,最初一个元素放在栈顶,之后指定一个 top 指针指向栈顶元素的地位当栈中只有一个元素是,此时 top=0 ,个别以 top 是否为 -1 来断定是否为空栈,当定义了栈的最大容量时,则栈顶 top 必须小于最大容量值上面咱们通过 Java 代码实现一个程序栈,非常简单如下:/** * @url: i-code.online * @author: 云栖简码 * @time: 2020/12/8 16:48 */public class Stack<T> { private Object[] stack; private int stackSize; private int top = -1; public Stack(int size){ stackSize = size; stack = new Object[size]; } public void push(T value){ if (top < stackSize-1){ top++; stack[top] = value; return; } throw new ArrayIndexOutOfBoundsException(top +"越界"); } public T pop(){ if (top > -1){ top--; return (T) stack[top+1]; } throw new ArrayIndexOutOfBoundsException(top +"越界"); } public boolean empty(){ return top == -1; }}当须要新增数据元素,即入栈操作时,就须要将新插入元素放在栈顶,并将栈顶指针减少 1。如下图所示: ...

December 9, 2020 · 2 min · jiezi

关于java-ee:CountDownLatchCyclicBarrierSemaphoreExchanger-的详细解析

本文次要介绍和比照咱们罕用的几种并发工具类,次要波及 CountDownLatch 、 CyclicBarrier 、 Semaphore 、 Exchanger 相干的内容,如果对多线程相干内容不相熟,能够看笔者之前的一些文章: 《Java并发编程-线程根底》《总算把线程六种状态的转换说分明了!》[《[高频面试]解释线程池的各个参数含意》](https://mp.weixin.qq.com/s/mX...《晓得线程池的四种回绝策略吗?》《java中常见的六种线程池详解》《基于synchronized的锁的深度解析》????举荐《JAVA中常见的阻塞队列详解》《优雅敞开线程池的计划》介绍 CountDownLatch 、CyclicBarrier 两者的应用与区别,他们都是期待多线程实现,是一种并发流程的管制伎俩,介绍 Semaphore、Exchanger 的应用,semaphore 是信号量,能够用来管制容许的线程数,而 Exchanger 能够用来替换两个线程间的数据。CountDownLatchCountDownLatch 是 JDK5 之后退出的一种并发流程管制工具,它在 java.util.concurrent 包下CountDownLatch 容许一个或多个线程期待其余线程实现操作,这里须要留神,是能够是一个期待也能够是多个来期待CountDownLatch 的构造函数如下,它承受一个 int 类型的参数作为计数器,即如果你想期待N 个线程实现,那么这里就传入 N public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }其中有两个外围的办法 countDown 与 await ,其中 当咱们调用 countDown 办法时相应的 N 的值减 1,而 await 办法则会阻塞以后线程,直到 N 的值变为零。说起来比拟形象,上面咱们通过理论案例来阐明。多个线程期待一个线程在咱们生存中最典型的案例就是体育中的跑步,假如当初咱们要进行一场赛跑,那么所有的选手都须要期待裁判员的起跑命令,这时候,咱们将其抽象化每个选手对应的是一个线程,而裁判员也是一个线程,那么就是多个选手的线程再期待裁判员线程的命令来执行咱们通过 CountDownLatch 来实现这一案例,那么期待的个数 N 就是下面的裁判线程的个数,即为 1, /** * @url i-code.onlien * 云栖简码 */ public static void main(String[] args) throws InterruptedException { //模仿跑步较量,裁判说开始,所有选手开始跑,咱们能够应用countDownlatch来实现 //这里须要期待裁判说开始,所以时等着一个线程 CountDownLatch countDownLatch = new CountDownLatch(1); new Thread(() ->{ try { System.out.println(Thread.currentThread().getName() +"已筹备"); countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"开始跑~~"); },"选手1").start(); new Thread(() ->{ try { System.out.println(Thread.currentThread().getName() +"已筹备"); countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"开始跑~~"); },"选手2").start(); TimeUnit.SECONDS.sleep(1); System.out.println("裁判:准备~~~"); countDownLatch.countDown(); System.out.println("裁判:跑~~~"); }运行后果如下: ...

November 30, 2020 · 5 min · jiezi

关于java-ee:优雅关闭线程池的方案

咱们常常在我的项目中应用的线程池,然而是否关怀过线程池的敞开呢,可能很多时候间接再我的项目中间接创立线程池让它始终运行当工作执行完结不在须要了也不去敞开,这其实是存在十分大的危险的,大量的线程常驻在后盾对系统资源的占用是微小的 ,甚至引发异样。所以在咱们平时应用线程池时须要留神优雅的敞开,这样能够保障资源的管控。在 Java 中和敞开线程池相干的办法次要有如下: void shutdown()List<Runnable> shutDownNowboolean awaitTerminationboolean isShutDownboolean isTerminated对于这些办法有着不同的应用和作用,上面咱们真的会这些不同的办法做具体的介绍。ShutDownshutDown 办法从字面意思咱们能够看到是进行敞开的意思,咱们先来看上面的一段代码,首先咱们通过 ThreadPoolExecutor 来创立一个容量是10的无界限程池,与 FixedThreadPool 相似的,这里手动创立能够更好地了解线程池的创立。在后咱们提交一千个工作执行,再执行 shutdown 办法进行暂停。 public static void main(String[] args) throws InterruptedException { ExecutorService service = new ThreadPoolExecutor( 10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); for (int i = 0; i < 1000; i++) { service.submit(() ->{ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("承受中断,不解决~~"); } System.out.println("args = " + Arrays.deepToString(args)+ Thread.currentThread().getName()); }); } service.shutdown(); }咱们能够看到后果所以线程会失常执行完结后再敞开线程池,对于 ShutDown 而言它能够平安的进行一个线程池,它有几个关键点ShutDown 会首先将线程设置成 SHUTDOWN 状态,而后中断所有没有正在运行的线程正在执行的线程和曾经在队列中的线程并不会被中断,说白了就是应用shutDown 办法其实就是要期待所有工作失常全副完结当前才会敞开线程池调用 shutdown() 办法后如果还有新的工作被提交,线程池则会依据回绝策略间接回绝后续新提交的工作。ShutDownNow这个办法与下面办法相比拟,直观就是 now ,即立刻进行工作,同样是上述案列,略作批改如下,public static void main(String[] args) throws InterruptedException { ExecutorService service = new ThreadPoolExecutor( 10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1000)); for (int i = 0; i < 1000; i++) { service.submit(() ->{ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("承受中断,完结线程~~"); //这里响应中断 return; } System.out.println("args = " + Arrays.deepToString(args)+ Thread.currentThread().getName()); }); } final List<Runnable> runnables = service.shutdownNow(); System.out.println(runnables); }执行上述代码咱们发现,当执行shutDownNow 办法后,会像全副正在运行的队列告诉中断,正在运行的线程接管到中断信号后抉择解决,而在队列中的全副勾销执行转移到一个list队列中返回,如上述 List<Runnable> runnables ,这里记录了所有终止的线程awaitTermination这个办法并不是用来敞开线程池的,首先咱们看一下这个办法的定义:boolean awaitTermination_(_long timeout, TimeUnit unit_)_能够看到这个办法有两个参数,timeout 示意期待的工夫,unit 工夫单位这个办法的作用是,调用后期待timeout工夫后,反馈线程池的状态,期待期间(包含进入期待状态之前)线程池已敞开并且所有已提交的工作(包含正在执行的和队列中期待的)都执行结束,相当于线程池曾经“终结”了,办法便会返回 true;期待超时工夫到后,第一种线程池“终结”的状况始终未产生,办法返回 false;期待期间线程被中断,办法会抛出 InterruptedException 异样。下面代码能够批改来测试,这里不再粘贴代码isShutDownisShutDown 办法正如名字,判断线程池是否进行,返回的是 Boolean 类型,如果曾经开始进行线程池则返回 true 否则放回false当调用了shutDown 或shutDownNow 时之后,会返回 true 不过须要留神,这时候只是代表线程池敞开流程的开始,并不是说线程池曾经进行了isTerminated这个办法与下面的办法的区别就是这是正真检测线程池是否真的终结了这不仅代表线程池已敞开,同时代表线程池中的所有工作都曾经都执行结束了,因为在调用 shutdown 办法之后,线程池会继续执行外面未实现的工作,包含正在执行的工作和在工作队列中期待的工作。如果调用了 shutdown 办法,然而有一个线程仍然在执行工作,那么此时调用 isShutdown 办法返回的是 true,而调用 isTerminated办法返回的便是 false,因为线程池中还有工作正在在被执行,线程池并没有真正“终结”。直到所有工作都执行结束了,调用 isTerminated() 办法才会返回 true,这示意线程池已敞开并且线程池外部是空的,所有残余的工作都执行结束了。本文由AnonyStar 公布,可转载但需申明原文出处。 欢送关注微信公账号 :云栖简码 获取更多优质文章 更多文章关注笔者博客 :云栖简码 i-code.online

November 23, 2020 · 1 min · jiezi

关于java-ee:2w长文带你剖析ConcurrentHashMap

并发编程实际中,ConcurrentHashMap是一个常常被应用的数据结构,相比于Hashtable以及Collections.synchronizedMap(),ConcurrentHashMap在线程平安的根底上提供了更好的写并发能力,但同时升高了对读一致性的要求(这点如同CAP实践啊 O(∩_∩)O)。ConcurrentHashMap的设计与实现十分精美,大量的利用了volatile,final,CAS等lock-free技术来缩小锁竞争对于性能的影响,无论对于Java并发编程的学习还是Java内存模型的了解,ConcurrentHashMap的设计以及源码都值得十分认真的浏览与琢磨。 这篇日志记录了本人对ConcurrentHashMap的一些总结,因为JDK6,7,8中实现都不同,须要离开论述在不同版本中的ConcurrentHashMap。 本文从源码登程,筛选集体感觉重要的点(会用红色标注)再次进行回顾,以及论述ConcurrentHashMap的一些留神点。 1. JDK6与JDK7中的实现1.1 设计思路ConcurrentHashMap采纳了分段锁的设计,只有在同一个分段内才存在竞态关系,不同的分段锁之间没有锁竞争。相比于对整个Map加锁的设计,分段锁大大的进步了高并发环境下的解决能力。但同时,因为不是对整个Map加锁,导致一些须要扫描整个Map的办法(如size(), containsValue())须要应用非凡的实现,另外一些办法(如clear())甚至放弃了对一致性的要求(ConcurrentHashMap是弱一致性的,具体请查看ConcurrentHashMap能齐全代替HashTable吗?)。 ConcurrentHashMap中的分段锁称为Segment,它即相似于HashMap(JDK7与JDK8中HashMap的实现)的构造,即外部领有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。ConcurrentHashMap中的HashEntry绝对于HashMap中的Entry有肯定的差异性:HashEntry中的value以及next都被volatile润饰,这样在多线程读写过程中可能放弃它们的可见性,代码如下: static final class HashEntry<K,V> { final int hash; final K key; volatile V value; volatile HashEntry<K,V> next;}1.2 并发度(Concurrency Level)并发度能够了解为程序运行时可能同时更新ConccurentHashMap且不产生锁竞争的最大线程数,实际上就是ConcurrentHashMap中的分段锁个数,即Segment[]的数组长度。ConcurrentHashMap默认的并发度为16,但用户也能够在构造函数中设置并发度。当用户设置并发度时,ConcurrentHashMap会应用大于等于该值的最小2幂指数作为理论并发度(如果用户设置并发度为17,理论并发度则为32)。运行时通过将key的高n位(n = 32 – segmentShift)和并发度减1(segmentMask)做位与运算定位到所在的Segment。segmentShift与segmentMask都是在结构过程中依据concurrency level被相应的计算出来。 如果并发度设置的过小,会带来重大的锁竞争问题;如果并发度设置的过大,本来位于同一个Segment内的拜访会扩散到不同的Segment中,CPU cache命中率会降落,从而引起程序性能降落。(文档的说法是依据你并发的线程数量决定,太多会导性能升高) 1.3 创立分段锁和JDK6不同,JDK7中除了第一个Segment之外,残余的Segments采纳的是提早初始化的机制:每次put之前都须要查看key对应的Segment是否为null,如果是则调用ensureSegment()以确保对应的Segment被创立。 ensureSegment可能在并发环境下被调用,但与设想中不同,ensureSegment并未应用锁来管制竞争,而是应用了Unsafe对象的getObjectVolatile()提供的原子读语义联合CAS来确保Segment创立的原子性。代码段如下: if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) { // recheck Segment<K,V> s = new Segment<K,V>(lf, threshold, tab); while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) { if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s)) break; }}1.4 put/putIfAbsent/putAll和JDK6一样,ConcurrentHashMap的put办法被代理到了对应的Segment(定位Segment的原理之前曾经形容过)中。与JDK6不同的是,JDK7版本的ConcurrentHashMap在取得Segment锁的过程中,做了肯定的优化 - 在真正申请锁之前,put办法会通过tryLock()办法尝试取得锁,在尝试取得锁的过程中会对对应hashcode的链表进行遍历,如果遍历结束依然找不到与key雷同的HashEntry节点,则为后续的put操作提前创立一个HashEntry。当tryLock肯定次数后仍无奈取得锁,则通过lock申请锁。 ...

November 19, 2020 · 12 min · jiezi

关于java-ee:JAVA中常见的阻塞队列详解

在之前的线程池的介绍中咱们看到了很多阻塞队列,这篇文章咱们次要来说说阻塞队列的事。阻塞队列也就是 BlockingQueue ,这个类是一个接口,同时继承了 Queue 接口,这两个接口都是在JDK5 中退出的 。BlockingQueue 阻塞队列是线程平安的,在咱们业务中是会常常频繁应用到的,如典型的生产者生产的场景,生产者只须要向队列中增加,而消费者负责从队列中获取。 如上图展现,咱们生产者线程一直的put 元素到队列,而消费者从中take 出元素解决,这样实现了工作与执行工作类之间的解耦,工作都被放入到了阻塞队列中,这样生产者和消费者之间就不会间接互相拜访实现了隔离进步了安全性。并发队列 下面是 Java 中队列Queue 类的类图,咱们能够看到它分为两大类,阻塞队列与非阻塞队列阻塞队列的实现接口是 BlockingQueue 而非阻塞队列的接口是 ConcurrentLinkedQueue , 本文次要介绍阻塞队列,非阻塞队列不再过多论述BlockingQueue 次要有上面六个实现类,别离是 ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、DelayQueue、PriorityBlockingQueue、LinkedTransferQueue 。这些阻塞队列有着各自的特点和实用场景,前面具体介绍。非阻塞队列的典型例子如 ConcurrentLinkedQueue , 它不会阻塞线程,而是利用了 CAS 来保障线程的平安。其实还有一个队列和 Queue 关系很严密,那就是Deque,这其实是 double-ended-queue 的缩写,意思是双端队列。它的特点是从头部和尾部都能增加和删除元素,而咱们常见的一般队列Queue 则是只能一端进一端出,即FIFO 。阻塞队列特点阻塞队列的特点就在于阻塞,它能够阻塞线程,让生产者消费者得以均衡,阻塞队列中有两个要害办法 Put 和 Take 办法take办法 take 办法的性能是获取并移除队列的头结点,通常在队列里有数据的时候是能够失常移除的。可是一旦执行 take 办法的时候,队列里无数据,则阻塞,直到队列里有数据。一旦队列里有数据了,就会立即解除阻塞状态,并且取到数据。过程如图所示:put办法 put 办法插入元素时,如果队列没有满,那就和一般的插入一样是失常的插入,然而如果队列已满,那么就无奈持续插入,则阻塞,直到队列里有了闲暇空间。如果后续队列有了闲暇空间,比方消费者生产了一个元素,那么此时队列就会解除阻塞状态,并把须要增加的数据增加到队列中。过程如图所示:是否有界(容量有多大) 此外,阻塞队列还有一个十分重要的属性,那就是容量的大小,分为有界和无界两种。无界队列意味着外面能够包容十分多的元素,例如 LinkedBlockingQueue 的下限是 Integer.MAX_VALUE,约为 2 的 31 次方,是十分大的一个数,能够近似认为是有限容量,因为咱们简直无奈把这个容量装满。然而有的阻塞队列是有界的,例如 ArrayBlockingQueue 如果容量满了,也不会扩容,所以一旦满了就无奈再往里放数据了。阻塞队列常见办法首先咱们从罕用的办法登程,依据各自的特点咱们能够大抵分为三个大类,如下表所示:分类办法含意特点抛出异样add增加一个元素如果队列已满,增加则抛出  IllegalStateException 异样 remove删除队列头节点当队列为空后,删除则抛出  NoSuchElementException 异样 element获取队列头元素当队列为空时,则抛出 NoSuchElementException 异样返回无异样offer增加一个元素当队列已满,不会报异样,返回  false ,如果胜利返回 true poll获取队列头节点,并且删除它当队列空时,返回  Null   peek单纯获取头节点当队列为空时反馈 NULL阻塞put增加一个元素如果队列已满则阻塞 take返回并删除头元素如果队列为空则阻塞如下面所示次要的八个办法,绝对都比较简单,上面咱们通过理论代码演示的形式来意识抛异样类型[add、remove、element]add向队列中增加一个元素。如果队列是有界队列,当队列已满时再增加则抛出异样提醒,如下: BlockingQueue queue = new ArrayBlockingQueue(2); queue.add(1); queue.add(2); queue.add(3);上述代码中咱们创立了一个阻塞队列容量为2,当咱们应用 add 向其中增加元素,当增加到第三个时则会抛出异样如下: ...

November 16, 2020 · 2 min · jiezi

关于java-ee:基于synchronized锁的深度解析

1. 问题引入小伙伴们都接触过线程,也都会应用线程,明天咱们要讲的是线程平安相干的内容,在这之前咱们先来看一个简略的代码案例。代码案例: /** * @url: i-code.online * @author: AnonyStar * @time: 2020/10/14 15:39 */public class ThreadSafaty { //共享变量 static int count = 0; public static void main(String[] args) { //创立线程 Runnable runnable = () -> { for (int i = 0; i < 5; i++) { count ++; try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }; for (int i = 0; i < 100; i++) { new Thread(runnable,"Thread-"+i).start(); } try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("count = "+ count); }}执行后果:问题阐明:在下面的代码中咱们能够看到,定义了一个线程 runnable 外面对公共成员变量进行 ++ 操作,并循环五次,每次睡眠一毫秒,之后咱们在主线程 main 办法中创立一百个线程并且启动,而后主线程睡眠期待五秒以此来等所有的线程执行完结。咱们预期后果应该是 500 。然而理论执行后咱们发现 count 的值是不固定的 ,是小于 500 的,这里就是多线程并行导致的数据安全性问题! ...

November 9, 2020 · 3 min · jiezi

关于java-ee:java中常见的六种线程池详解

之前咱们介绍了线程池的四种回绝策略,理解了线程池参数的含意,那么明天咱们来聊聊Java 中常见的几种线程池,以及在jdk7 退出的 ForkJoin 新型线程池首先咱们列出Java 中的六种线程池如下线程池名称形容FixedThreadPool外围线程数与最大线程数雷同SingleThreadExecutor一个线程的线程池CachedThreadPool外围线程为0,最大线程数为Integer. MAX_VALUEScheduledThreadPool指定外围线程数的定时线程池SingleThreadScheduledExecutor单例的定时线程池ForkJoinPoolJDK 7 新退出的一种线程池在理解集中线程池时咱们先来相熟一下次要几个类的关系,ThreadPoolExecutor 的类图,以及 Executors 的次要办法: 下面看到的类图,不便帮忙上面的了解和查看,咱们能够看到一个外围类 ExecutorService , 这是咱们线程池都实现的基类,咱们接下来说的都是它的实现类。FixedThreadPoolFixedThreadPool 线程池的特点是它的外围线程数和最大线程数一样,咱们能够看它的实现代码在 Executors#newFixedThreadPool(int) 中,如下: public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }咱们能够看到办法内创立线程调用的理论是 ThreadPoolExecutor 类,这是线程池的外围执行器,传入的 nThread 参数作为外围线程数和最大线程数传入,队列采纳了一个链表构造的有界队列。这种线程池咱们能够看作是固定线程数的线程池,它只有在开始初始化的时候线程数会从0开始创立,然而创立好后就不再销毁,而是全副作为常驻线程池,这里如果对线程池参数不了解的能够看之前文章 《解释线程池各个参数的含意》。对于这种线程池他的第三个和第四个参数是没意义,它们是闲暇线程存活工夫,这里都是常驻不存在销毁,当线程解决不了时会退出到阻塞队列,这是一个链表构造的有界阻塞队列,最大长度是Integer. MAX_VALUESingleThreadExecutorSingleThreadExecutor 线程的特点是它的外围线程数和最大线程数均为1,咱们也能够将其工作是一个单例线程池,它的实现代码是Executors#newSingleThreadExcutor() , 如下: public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); }上述代码中咱们发现它有一个重载函数,传入了一个ThreadFactory 的参数,个别在咱们开发中会传入咱们自定义的线程创立工厂,如果不传入则会调用默认的线程工厂咱们能够看到它与 FixedThreadPool 线程池的区别仅仅是外围线程数和最大线程数改为了1,也就是说不论工作多少,它只会有惟一的一个线程去执行如果在执行过程中产生异样等导致线程销毁,线程池也会从新创立一个线程来执行后续的工作这种线程池非常适合所有工作都须要按被提交的程序来执行的场景,是个单线程的串行。CachedThreadPoolcachedThreadPool 线程池的特点是它的常驻外围线程数为0,正如其名字一样,它所有的县城都是长期的创立,对于它的实现在 Executors#newCachedThreadPool() 中,代码如下: public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }从上述代码中咱们能够看到 CachedThreadPool 线程池中,最大线程数为 Integer.MAX_VALUE , 意味着他的线程数简直能够有限减少。因为创立的线程都是长期线程,所以他们都会被销毁,这里闲暇 线程销毁工夫是60秒,也就是说当线程在60秒内没有工作执行则销毁这里咱们须要留神点,它应用了 SynchronousQueue 的一个阻塞队列来存储工作,这个队列是无奈存储的,因为他的容量为0,它只负责对工作的传递和直达,效率会更高,因为外围线程都为0,这个队列如果存储工作不存在意义。ScheduledThreadPoolScheduledThreadPool 线程池是反对定时或者周期性执行工作,他的创立代码 Executors.newSchedsuledThreadPool(int) 中,如下所示: public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory) { return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); }咱们发现这里调用了 ScheduledThreadPoolExecutor 这个类的构造函数,进一步查看发现 ScheduledThreadPoolExecutor 类是一个继承了 ThreadPoolExecutor 的,同时实现了 ScheduledExecutorService 接口,咱们看到它的几个构造函数都是调用父类 ThreadPoolExecutor 的构造函数 public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); } public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory); } public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), handler); } public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory, handler); }从下面代码咱们能够看到和其余线程池创立并没有差别,只是这里的工作队列是 DelayedWorkQueue 对于阻塞丢列咱们下篇文章专门说,这里咱们先创立一个周期性的线程池来看一下 public static void main(String[] args) { ScheduledExecutorService service = Executors.newScheduledThreadPool(5); // 1. 提早肯定工夫执行一次 service.schedule(() ->{ System.out.println("schedule ==> 云栖简码-i-code.online"); },2, TimeUnit.SECONDS); // 2. 依照固定频率周期执行 service.scheduleAtFixedRate(() ->{ System.out.println("scheduleAtFixedRate ==> 云栖简码-i-code.online"); },2,3,TimeUnit.SECONDS); //3. 依照固定频率周期执行 service.scheduleWithFixedDelay(() -> { System.out.println("scheduleWithFixedDelay ==> 云栖简码-i-code.online"); },2,5,TimeUnit.SECONDS); }下面代码是咱们简略创立了 newScheduledThreadPool ,同时演示了外面的三个外围办法,首先看执行的后果: ...

November 3, 2020 · 4 min · jiezi

关于java-ee:知道线程池的四种拒绝策略吗

在之前的文章中咱们晓得了线程池各个参数的含意,其中有个参数handler 咱们说了是回绝策略,具体对于线程池的回绝策略咱们这篇文章来剖析首先咱们要了解线程池的回绝策略的作用,它是用来解决当线程池无奈持续解决更多的工作时的解决机制,那么首先咱们要晓得回绝策略的触发机会,我么们来看上面代码: ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 2, 1, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.DiscardOldestPolicy() ); 线程池的回绝机会第一种,当咱们失常敞开线程池时也就是应用shutdown 等办法,这时候即便线程池中还有未实现的工作正在执行,然而因为线程池曾经敞开,所以这时候咱们再持续向线程池提交工作的话就会被回绝第二种,当线程池没有能力再持续解决提交的工作,如队列已满,最大线程数达到,这时候线程池就处于饱和状态 在咱们须要学习和理解的就是第二种,在理论开发中咱们须要解决这种状况,对于线程池运行的流程能够看之前咱们文章,也可关注 i-code.online 博客。 回绝策略首先咱们晓得线程池的回绝策略参数的类型是 RejectedExecutionHandler 类型的,那么咱们能够先来理解一下对于这个接口的关系 在上述类图中咱们能够看到 RejectedExecutionHandler 接口有四个实现类,同时都提供了无参构造函数,这四个实现类对应了不同的回绝策略,都有各自的实用场景AbortPolicy 回绝策略:这种策略在回绝工作时,会间接抛出一个类型为 RejectedExecutionException 的 RuntimeException,让你感知到工作被回绝了,于是你便能够依据业务逻辑抉择重试或者放弃提交等策略。DiscardPolicy 回绝策略:这种策略是当工作提交时间接将刚提交的工作抛弃,而且不会给与任何提醒告诉,所以这种策略应用要谨慎,因为有肯定的危险,对咱们来说基本不晓得提交的工作有没有被抛弃DiscardOldestPolicy 回绝策略:这种策略和下面类似。不过它抛弃的是队列中的头节点,也就是存活工夫最久的 CallerRunsPolicy 回绝策略:这种策略算是最欠缺的绝对于其余三个,当线程池无能力解决当前任务时,会将这个工作的执行权交予提交工作的线程来执行,也就是谁提交谁负责,这样的话提交的工作就不会被抛弃而造成业务损失,同时这种谁提交谁负责的策略必须让提交线程来负责执行,如果工作比拟耗时,那么这段时间内提交工作的线程也会处于繁忙状态而无奈持续提交工作,这样也就减缓了工作的提交速度,这相当于一个负反馈。也有利于线程池中的线程来消化工作 本文由AnonyStar 公布,可转载但需申明原文出处。 欢送关注微信公账号 :云栖简码 获取更多优质文章 更多文章关注笔者博客 :云栖简码 i-code.online

October 29, 2020 · 1 min · jiezi

关于java-ee:小程序商城Mall打造最佳SpringCloudAlibaba最佳实践

背景 因为一路一来看过很多的技术体系,也见证一些技术体系进行保护,想用本人感觉比拟好的一套技术体系来做一个散布式微服务零碎,包含开发层面,中间件层面和运维层面的技术,作为本人心愿的一个技术团队里的技术栈。这年SpringCloud开始把以前的技术给进行保护了,如eureka、feign、zuul,还有当当网的那个elastic-job,当当网都要挂了。所以出于这些起因,想换一套好使的技术栈。 而且我看了其余的商城的我的项目,要么就是不应用微服务的,要么就是只写了后端或者前端的,或者是常识纯正用技术把性能给写进去,搬砖实现玩性能,做完而已,所以还是想本人依照实在一点的样子来开发,试着解决和做如何牢靠和稳固分布式服务计划。 本文目标给大家介绍一下整体的设计和某些设计时,所用那款技术栈的起因。介绍开发小程序所用工具,着手前端开发当前逐个介绍每个技术场景的要点、技术难点一些货色只是在开发中,也还没实现。本文也是为了本人写一下本人喜爱的技术,留个底。欢送关注公众号,文章更快一步我的公众号 :地藏思维 技术架构这次次要转站SpringCloudAlibaba 服务治理:springCloud Dubbo,因为SpringCloud原来那套都进行保护了。尽管这次用回dubbo了,然而有些人说问是否悔恨从dubbo转springCloud,不会悔恨呀,因为那时候dubbo只有服务调度治理性能,没啥周边配套的。服务降级也是因为Hystrix不必了,所以用Sentinel 服务网关:以前呆过的公司,是由一个java服务作为app的服务端,再聚合上游多个服务再提供给接口包一层返回给app的,还有那些鉴权性能,这次应用SpringCloud gateway转发和鉴权。鉴权局部换了形式,这个点比拟要害,放到前面业务零碎设计来说。 音讯队列:应用RocketMq,而不必rabbitMq(慢),也不必Kafka(没有tag分标签的性能,一个队列多个消费者会生产雷同的货色) 分布式事务:就是跨零碎的事务,以前都是应用最终一致性,异步解决的形式,这次看有没有场景应用到分布式同步的事务,之前理解的是用LCN国人开发的框架,这次想用阿里的Seata。(目前开发中的还没有应用上) 规定引擎:图上没写,这里说一下是应用drools,就是用于营销服务的,是做那个店铺里的各个优惠的规定,如:满100减去10,满3件减10块,满多少送1个物品的这些规定。其实都能够用规定引擎来做,以前呆过的网贷公司,那时是用于对接n第三方时不同规定。 定时任务调度:SchedulerX,而不必elastic-job,因为当当要挂了。唯品会用elastic-job封装了一下搞两个叫saturn,而虎牙又把saturn封装,这次调研一下SchedulerX,看看哪个好使。 自研主动生成RestFul接口:这个是我本人做的一个框架,concise-mvc,简洁mvc生成器,因为微服务当初都是用dubbo调用了,没有http协定的接口给springCloudgateway或者下层利用应用。所以搞了个依据某个特制的注解下的interface,都生成:/ #{interface名字}/#{办法名字}的uri。这样开发者就能够只开发dubbo接口,主动生成好http接口让gateway来调用。 前端:前端目前只做小程序端,不做pc端,这次前端也是我本人写,应用uniapp,这个框架呢是能够用一份代码,实现微信、抖音、百度等小程序的编译,他编译会主动转换为他们须要的接口。为什么不必他来实现pc端的前端代码,是因为pc比拟大,款式布局就不一样了。 后盾管理系统是应用vueadmin的框架,也是跟uniapp那样给你做好了导航栏那些,跳转那些。在外面写实现就好了。 运维设施方面技术栈是我在贷款公司里感觉比拟好使的一些组件,前面去的公司也很多都用那些,其中最好的是以前贷款公司基于k8s开发的零碎,因为其余的公司搞的k8s部署就只能一个test环境和prod环境。 其实那框外面很多都是本我的项目中我不会去做的,因为没有工夫做太多,可能会找他人帮忙部署。微服务划分 UDB零碎我看很多个零碎里都有个叫security的服务,然而他们只是做鉴权性能。而这里为什么叫udb,是因为我在一个互联网公司的时候这个零碎不只是仅仅的鉴权,而是做了对立帐号的性能,就是让微信、手机登陆、QQ的受权登录,都对立一个中央,让雷同的人的微信号、qq号都绑定为一个号码。并且生成出一个id,作为userId,让业务其余零碎都通过此id对立来交互。 微信受权其实也是oauth2的。这样对立个鉴权的中央,生成的token就能够蕴含userId,这个使得开发springCloudgateway,能够应用GatewayFilter对立拦挡数据的接口,filter里先做鉴权,让udb零碎返回userid,而后把userid,塞到Http的body外面,让gateway上游零碎应用这个userId,这样就能保障用户本人能力批改本人的数据,不会批改了他人id的数据,除非你把token都给他人了。 通过这样的联合Udb零碎和springCloudgateway,就能够对立userId交互,对立鉴权,别各个我的项目,如一时电商一时点餐,当雷同的人的时候就一个userId不同appId辨别就好了,不然一个人来雷同的企业有不同的身份证号,你看工商银行app和e融app外面也是雷同的自然人号,不同的业务id。营销服务这里包含优惠券,促销流动,商家流动的优惠形式,其中上文说到的店铺优惠,一时满100减去10,一时满3件减10块,如果你用传统形式,都存到表里叫商店优惠规定,一个商店就有多个规定,那你优惠条件有很多种,每种又有不同后果,在代码里if else很麻烦的,所以先倡议应用drools,后再讲怎么用。 其余的服务我感觉很惯例,就看扣钱扣商品的时候是最终一致性还是要做分布式事务那些不具体讲了。 已开发实现样例 结语总有一天大家也会独立实现本人喜爱的零碎,搭建本人的玩具。 欢送关注公众号,文章更快一步我的公众号 :地藏思维 掘金:地藏Kelvin 简书:地藏Kelvin 我的Gitee: 地藏Kelvin https://gitee.com/kelvin-cai 本文由博客一文多发平台 OpenWrite 公布!

October 25, 2020 · 1 min · jiezi

关于java-ee:总算把线程六种状态的转换说清楚了

在咱们接触编程时,就开始接触各种生命周期,比方对象的生命周期,程序的生命周期等等,对于线程来说也是存在本人的生命周期,而且这也是面试与咱们深刻理解多线程必备的常识,明天咱们次要介绍线程的生命周期及其各种状态的转换。 线程的六种状态线程的生命周期次要有以下六种状态: New(新创建)Runnable(可运行)Blocked(被阻塞)Waiting(期待)Timed Waiting(计时期待)Terminated(被终止)在咱们程序编码中如果想要确定线程以后的状态,能够通过 getState() 办法来获取,同时咱们须要留神任何线程在任何时刻都只能是处于一种状态。 New 新建状态             首先咱们展现一下整个线程状态的转换流程图,上面咱们将进行具体的介绍解说,如下图所示,咱们能够直观的看到六种状态的转换,首先左侧上方是 NEW 状态,这是创立新线程的状态,相当于咱们 new Thread() 的过程。 New 示意线程被创立但尚未启动的状态:当咱们用 new Thread() 新建一个线程时,如果线程没有开始运行 start() 办法,那么线程也就没有开始执行 run() 办法外面的代码,那么此时它的状态就是 New。而一旦线程调用了 start(),它的状态就会从 New 变成 Runnable,进入到图中绿色的方框 Runnable 可运行状态Java 中的 **Runable ** 状态对应操作系统线程状态中的两种状态,别离是 Running 和 Ready,也就是说,Java 中处于 Runnable 状态的线程有可能正在执行,也有可能没有正在执行,正在期待被调配 CPU 资源。所以,如果一个正在运行的线程是 Runnable 状态,当它运行到工作的一半时,执行该线程的 CPU 被调度去做其余事件,导致该线程临时不运行,它的状态仍然不变,还是 Runnable,因为它有可能随时被调度回来继续执行工作。阻塞状态 下面意识了线程的要害状态 Runnable ,那么接下来咱们来看一下上面的三个状态,这三个状态咱们能够统称为阻塞状态,它们别离是 Blocked(被阻塞)、Waiting(期待)、Timed Waiting(计时期待) . Blocked 被阻塞状态首先咱们来认识一下 Blocked 状态,这是一个绝对简略的状态,咱们能够通过上面的图示看到,从 Runnable 状态进入到 Blocked 状态只有一种路径,那么就是当进入到 synchronized 代码块中时未能取得相应的 monitor 锁(对于 monitor 锁咱们在之后专门来介绍,这里咱们晓得 synchronized 的实现都是基于 monitor 锁的), ...

October 19, 2020 · 2 min · jiezi

关于java-ee:如何优雅的停止一个线程

在之前的文章中 i-code.online -《并发编程-线程根底》咱们介绍了线程的创立和终止,从源码的角度去了解了其中的细节,那么当初如果面试有人问你 “如何优雅的进行一个线程?”, 你该如何去答复尼 ?能不能完满的答复尼? 对于线程的进行,通常状况下咱们是不会去手动去进行的,而是期待线程天然运行至完结进行,然而在咱们理论开发中,会有很多状况中咱们是须要提前去手动来进行线程,比方程序中出现异常谬误,比方使用者关闭程序等状况中。在这些场景下如果不能很好地进行线程那么就会导致各种问题,所以正确的进行程序是十分的重要的。 强行进行线程会怎么? 在咱们平时的开发中咱们很多时候都不会留神线程是否是强壮的,是否能优雅的进行,很多状况下都是贸然的强制进行正在运行的线程,这样可能会造成一些平安问题,为了防止造成这种损失,咱们应该给与线程适当的工夫来解决完以后线程的收尾工作, 而不至于影响咱们的业务。对于 Java 而言,最正确的进行线程的形式是应用 interrupt。但 interrupt 仅仅起到告诉被进行线程的作用。而对于被进行的线程而言,它领有齐全的自主权,它既能够抉择立刻进行,也能够抉择一段时间后进行,也能够抉择压根不进行。可能很多同学会纳闷,既然这样那这个存在的意义有什么尼,其实对于 Java 而言,冀望程序之间是可能互相告诉、合作的治理线程比方咱们有线程在进行 io 操作时,当程序正在进行写文件奥做,这时候接管到终止线程的信号,那么它不会立马进行,它会依据本身业务来判断该如何解决,是将整个文件写入胜利后在进行还是不进行等都取决于被告诉线程的解决。如果这里立马终止线程就可能造成数据的不完整性,这是咱们业务所不心愿的后果。  interrupt 进行线程对于 interrupt 的应用咱们不在这里过多论述,能够看 i-code.online -《并发编程-线程根底》文中的介绍,其外围就是通过调用线程的 isInterrupt() 办法进而判断中断信号,当线程检测到为 true 时则阐明接管到终止信号,此时咱们须要做相应的解决 咱们编写一个简略例子来看 Thread thread = new Thread(() -> { while (true) { //判断以后线程是否中断, if (Thread.currentThread().isInterrupted()) { System.out.println("线程1 接管到中断信息,中断线程...中断标记:" + Thread.currentThread().isInterrupted()); //跳出循环,完结线程 break; } System.out.println(Thread.currentThread().getName() + "线程正在执行..."); } }, "interrupt-1"); //启动线程 1 thread.start(); //创立 interrupt-2 线程 new Thread(() -> { int i = 0; while (i <20){ System.out.println(Thread.currentThread().getName()+"线程正在执行..."); if (i == 8){ System.out.println("设置线程中断...." ); //告诉线程1 设置中断告诉 thread.interrupt(); } i ++; try { TimeUnit.MILLISECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } },"interrupt-2").start();上述代码绝对比较简单,咱们创立了两个线程,第一个线程咱们其中做了中断信号检测,当接管到中断请求则完结循环,天然的终止线程,在线程二中,咱们模仿当执行到 i==8 时告诉线程一终止,这种状况下咱们能够看到程序天然的进行的终止。 ...

October 12, 2020 · 3 min · jiezi

关于java-ee:Java并发编程线程基础

1. 线程的创立首先咱们来温习咱们学习 java 时接触的线程创立,这也是面试的时候喜爱问的,有人说两种也有人说三种四种等等,其实咱们不能去死记硬背,而应该深刻了解其中的原理,当咱们了解后就会发现所谓的创立线程本质都是一样的,在咱们面试的过程中如果咱们能从实质登程答复这样的问题,那么置信肯定是个加分项!好了咱们不多说了,开始明天的 code 之路 1.1 继承 Thread 类创立线程 ** 这是咱们最常见的创立线程的形式,通过继承 Thread 类来重写 run 办法,代码如下: /** * 线程类 * url: www.i-code.online * @author: anonyStar * @time: 2020/9/24 18:55 */public class ThreadDemo extends Thread { @Override public void run() { //线程执行内容 while (true){ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThredDemo 线程正在执行,线程名:"+ Thread.currentThread().getName()); } }}测试方法: @Test public void thread01(){ Thread thread = new ThreadDemo(); thread.setName("线程-1 "); thread.start(); while (true){ System.out.println("这是main主线程:" + Thread.currentThread().getName()); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }后果: ...

October 10, 2020 · 7 min · jiezi

关于java-ee:下载JDK-与-Hotspot-虚拟机源码

下载 jdk 源码,与 jvm 虚拟机源码,虚拟机当初支流都是 hotspot虚拟机  首先进入 openjdk 官方网站 http://openjdk.java.net/ ,这个网站中咱们能够下载 jdk 源码,也能够下载 hotspot 虚拟机的源码  首先咱们进入 Mercurial 菜单下,这是一个版本控制器相似 git 、SVN 之类  如上图所示,咱们能够看到下面列了很多我的项目,咱们这里抉择 jdk8u 的我的项目进入  进入后能够间接在线看,也能够间接下载压缩包,这里举荐下载压缩包,本地来查看  下载后解压,应用编辑器即可进行查看,比方应用 vscode 软件间接查阅,之后文章会介绍对于 jdk 源码在本地编译构建的内容 本文由AnonyStar 公布,可转载但需申明原文出处。 企慕「优雅编码的艺术」 深信游刃有余,致力扭转人生 欢送关注微信公账号 :云栖简码 获取更多优质文章 更多文章关注笔者博客 :云栖简码

September 29, 2020 · 1 min · jiezi

关于java-ee:Java操作Elasticsearch-之-Java-High-Level-REST-Clientedit

1. 简述Elasticsearch 是基于 Lucene 开发的一个分布式全文检索框架,向 Elasticsearch 中存储和从 Elasticsearch 中查问,格局是json。向 Elasticsearch 中存储数据,其实就是向 es 中的 index 上面的 type 中存储 json 类型的数据。elasticsearch 提供了很多语言的客户端用于操作 elasticsearch 服务,例如: java 、 python 、 .net 、 JavaScript 、 PHP 等。本文次要介绍如何应用 java 语言来操作 elasticsearch 服务。在 elasticsearch 的官网上提供了两种 java 语言的 API ,一种是 Java Transport Client,一种是 Java REST Client。Java Transport Client 是基于 TCP 协定交互的,在 elasticsearch 7.0+ 版本后官网不再赞成应用,在Elasticsearch 8.0的版本中齐全移除 TransportClient Java REST Client 是基于 HTTP 协定交互,而 Java REST Client 又分为 Java Low Level REST Client 和 Java High Level REST Client ...

September 23, 2020 · 8 min · jiezi

关于java-ee:spring源码解读-IoC-之解析-import-标签

在上一文中咱们剖析了注册 BeanDefinition 的过程,在其中咱们理解到在解析跟节点和子节点时候两种状况,对于默认名称空间的标签咱们通过 DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 进行解决,而对于自定义标签则通过 BeanDefinitionParserDelegate#parseCustomElement(Element ele) 办法进行解决。这里咱们首先对默认名称空间的解析进行开始解读, #parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 办法的代码如下: public static final String NESTED_BEANS_ELEMENT = "beans"; public static final String ALIAS_ELEMENT = "alias"; public static final String NAME_ATTRIBUTE = "name"; public static final String ALIAS_ATTRIBUTE = "alias"; public static final String IMPORT_ELEMENT = "import"; /** * 如果根节点或者子节点采纳默认命名空间的话 采纳默认的解析形式 * @param ele * @param delegate */ private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { // import 标签 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { //alias 标签 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { //解决 bean 标签 这是spring中很外围的标签解决 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // 解决 beans 标签 doRegisterBeanDefinitions(ele); } }通过上述代码咱们能够的看到默认标签蕴含 import 、 alias 、 bean 、 beans , 本文将对 import 的解析进行解读 ...

September 2, 2020 · 4 min · jiezi

关于java-ee:spring源码解读-ioc之验证模型获取

咱们上一篇文章最初调用到 `org.springframework.beans.factory.xml. XmlBeanDefinitionReader#doLoadDocument(...) ` 办法,该办法次要代码如下: protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { //getValidationModeForResource是数据验证模型 return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }咱们这篇文章次要以 #getValidationModeForResource(...) 办法作为切入,来剖析一下验证模型的次要办法,对于spring中的数据验证、绑定等内容咱们在前面文章一点点的来开掘 1.getValidationModeForResource/** * 禁止只用验证模型 * Indicates that the validation should be disabled. */ public static final int VALIDATION_NONE = XmlValidationModeDetector.VALIDATION_NONE; /** * 应用自动检测验证模型 * Indicates that the validation mode should be detected automatically. */ public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO; /** * 应用DTD验证模型 * Indicates that DTD validation should be used. */ public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD; /** * 应用XSD验证模型 * Indicates that XSD validation should be used. */ public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD; /** * 确定以后 resource资源须要的验证模型 默认主动模式 * 如果显式的配置了验证模型,则用配置的,如果没有则从Resource中来获取 * Determine the validation mode for the specified {@link Resource}. * If no explicit validation mode has been configured, then the validation * mode gets {@link #detectValidationMode detected} from the given resource. * <p>Override this method if you would like full control over the validation * mode, even when something other than {@link #VALIDATION_AUTO} was set. * @see #detectValidationMode */ protected int getValidationModeForResource(Resource resource) { // <1> 获取指定的验证模式 int validationModeToUse = getValidationMode(); // <2> 如果有指定的模式 则间接返回, if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } //<3> 没有指定的 那么通resource中去获取 int detectedMode = detectValidationMode(resource); if (detectedMode != VALIDATION_AUTO) { return detectedMode; } // <4> 若以上都不满足则应用XSD模式 // Hmm, we didn't get a clear indication... Let's assume XSD, // since apparently no DTD declaration has been found up until // detection stopped (before finding the document's root tag). return VALIDATION_XSD; }对上述代码咱们做简略剖析 ...

August 28, 2020 · 5 min · jiezi

关于java-ee:Jakarta-EE-Java-EE的终结者

什么是 Jakarta EEJakarta EE并不是新技术,他的前身就是大家相熟的Java EE,老一辈的程序员可能还记得J2EE,是的,他们都是同一个货色,至于为什么会改来改去,这外面就有很多故事了。 1998年12月,SUN公司公布了JDK1.2,开始应用Java 2 这一名称,第二年Sun公司联结IBM、Oracle、BEA等大型企业应用零碎开发商独特制订了一个基于Java组件技术的企业应用零碎开发标准,名字很天然就取为Java 2 Platform Enterprise Edition简称J2EE,前面的事件大家也晓得,JDK版本升的很快,J2EE名称如果跟着Java版本走必然会给开发人员造成困惑不利于该技术的推广,终于在2006年,SUN公司在公布Java 5后正式将J2EE改名为Java EE(Java Platform, Enterprise Edition),很多晚期j2ee开发者尽管当初用的是最新的java ee规范但他们还是认为本人在用j2ee,当然,只是名称的扭转并没有给开发者带来什么麻烦,相比之下上面这个就是要命。 2009年,Oracle发表收买SUN,Java相干技术天然归Oracle所有,在2017年,Oracle 发表开源 Java EE 并将我的项目移交给 Eclipse 基金会,但Oracle移交的很不畅快,提了很多要求,其中就包含不能再应用Java EE这个名称,尽管有点无理但Eclipse基金会还是承受了这个要求并且改名为Jakarta EE,经验了从j2ee到java ee的改名再经验一次仿佛也无所谓,但要命的是Oracle要求Jakarta EE不能批改javax命名空间,这个就意味着java ee移交后代码就此封版,如果想批改代码,不好意思,请重整旗鼓,所以,Oracle你移交的意义在哪? 那么从Java EE到Jakarta EE会给企业带来什么影响?上面咱们一起剖析。 名称的转变这个对企业影响不大,对开发者影响也不大,你能够欢快用着Jakarta EE最新规范的同时跟共事说java ee公布新版本了。 命名空间的转变如果你是用Maven进行开发,那么Java EE的依赖是这么定义的 <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0.1</version> <scope>provided</scope></dependency>咱们能够看到groupId是javax,并且源码的构造如下: .└── javax ├── annotation ├── batch ├── decorator ├── ejb ├── el ├── enterprise ├── faces ├── inject ├── interceptor ├── jms ├── json ├── mail ├── management ├── persistence ├── resource ├── security ├── servlet ├── transaction ├── validation ├── websocket ├── ws └── xml所有的源码都定义在javax.*这个门路下,依据Oracle的要求Jakarta EE不能批改javax命名空间,那么Jakarta EE只能将代码中javax批改为jakarta,因而最新版本Jakarta Maven形容是长这样的: ...

July 30, 2020 · 1 min · jiezi

关于java-ee:Jakarta-EE-Java-EE的终结者

什么是 Jakarta EEJakarta EE并不是新技术,他的前身就是大家相熟的Java EE,老一辈的程序员可能还记得J2EE,是的,他们都是同一个货色,至于为什么会改来改去,这外面就有很多故事了。 1998年12月,SUN公司公布了JDK1.2,开始应用Java 2 这一名称,第二年Sun公司联结IBM、Oracle、BEA等大型企业应用零碎开发商独特制订了一个基于Java组件技术的企业应用零碎开发标准,名字很天然就取为Java 2 Platform Enterprise Edition简称J2EE,前面的事件大家也晓得,JDK版本升的很快,J2EE名称如果跟着Java版本走必然会给开发人员造成困惑不利于该技术的推广,终于在2006年,SUN公司在公布Java 5后正式将J2EE改名为Java EE(Java Platform, Enterprise Edition),很多晚期j2ee开发者尽管当初用的是最新的java ee规范但他们还是认为本人在用j2ee,当然,只是名称的扭转并没有给开发者带来什么麻烦,相比之下上面这个就是要命。 2009年,Oracle发表收买SUN,Java相干技术天然归Oracle所有,在2017年,Oracle 发表开源 Java EE 并将我的项目移交给 Eclipse 基金会,但Oracle移交的很不畅快,提了很多要求,其中就包含不能再应用Java EE这个名称,尽管有点无理但Eclipse基金会还是承受了这个要求并且改名为Jakarta EE,经验了从j2ee到java ee的改名再经验一次仿佛也无所谓,但要命的是Oracle要求Jakarta EE不能批改javax命名空间,这个就意味着java ee移交后代码就此封版,如果想批改代码,不好意思,请重整旗鼓,所以,Oracle你移交的意义在哪? 那么从Java EE到Jakarta EE会给企业带来什么影响?上面咱们一起剖析。 名称的转变这个对企业影响不大,对开发者影响也不大,你能够欢快用着Jakarta EE最新规范的同时跟共事说java ee公布新版本了。 命名空间的转变如果你是用Maven进行开发,那么Java EE的依赖是这么定义的 <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0.1</version> <scope>provided</scope></dependency>咱们能够看到groupId是javax,并且源码的构造如下: .└── javax ├── annotation ├── batch ├── decorator ├── ejb ├── el ├── enterprise ├── faces ├── inject ├── interceptor ├── jms ├── json ├── mail ├── management ├── persistence ├── resource ├── security ├── servlet ├── transaction ├── validation ├── websocket ├── ws └── xml所有的源码都定义在javax.*这个门路下,依据Oracle的要求Jakarta EE不能批改javax命名空间,那么Jakarta EE只能将代码中javax批改为jakarta,因而最新版本Jakarta Maven形容是长这样的: ...

July 30, 2020 · 1 min · jiezi

关于java-ee:限流算法与Guava-RateLimiter解析

在分布式系统中,应答高并发拜访时,缓存、限流、降级是爱护零碎失常运行的罕用办法。当申请量突发暴涨时,如果不加以限度拜访,则可能导致整个零碎解体,服务不可用。同时有一些业务场景,比方短信验证码,或者其它第三方API调用,也须要提供必要的拜访限度反对。还有一些资源耗费过大的申请,比方数据导出等(参考 记一次线上Java服务CPU 100%处理过程 ),也有限度拜访频率的需要。 常见的限流算法有令牌桶算法,漏桶算法,与计数器算法。本文次要对三个算法的基本原理及Google Guava包中令牌桶算法的实现RateLimiter进行介绍,下一篇文章介绍最近写的一个以RateLimiter为参考的分布式限流实现及计数器限流实现。 令牌桶算法令牌桶算法的原理就是以一个恒定的速度往桶里放入令牌,每一个申请的解决都须要从桶里先获取一个令牌,当桶里没有令牌时,则申请不会被解决,要么排队期待,要么降级解决,要么间接拒绝服务。当桶里令牌满时,新增加的令牌会被抛弃或回绝。 令牌桶算法的解决示意图如下(图片来自网络) 令牌桶算法次要是能够管制申请的均匀解决速率,它容许预生产,即能够提前生产令牌,以应答突发申请,然而前面的申请须要为预生产买单(期待更长的工夫),以满足申请解决的均匀速率是肯定的。 漏桶算法漏桶算法的原理是水(申请)先进入漏桶中,漏桶以肯定的速度出水(解决申请),当水流入速度大于流出速度导致水在桶内逐步沉积直到桶满时,水会溢出(申请被回绝)。 漏桶算法的解决示意图如下(图片来自网络) 漏桶算法次要是管制申请的解决速率,平滑网络上的突发流量,申请能够以任意速度进入漏桶中,但申请的解决则以恒定的速度进行。 计数器算法计数器算法是限流算法中最简略的一种算法,限度在一个工夫窗口内,至少解决多少个申请。比方每分钟最多解决10个申请,则从第一个申请进来的工夫为终点,60s的工夫窗口内只容许最多解决10个申请。下一个工夫窗口又以前一时间窗口过后第一个申请进来的工夫为终点。常见的比方一分钟内只能获取一次短信验证码的性能能够通过计数器算法来实现。 Guava RateLimiter解析Guava是Google开源的一个工具包,其中的RateLimiter是实现了令牌桶算法的一个限流工具类。在pom.xml中增加guava依赖,即可应用RateLimiter <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version></dependency>如下测试代码示例了RateLimiter的用法, public static void main(String[] args) { RateLimiter rateLimiter = RateLimiter.create(1); //创立一个每秒产生一个令牌的令牌桶 for(int i=1;i<=5;i++) { double waitTime = rateLimiter.acquire(i); //一次获取i个令牌 System.out.println("acquire:" + i + " waitTime:" + waitTime); }}运行后,输入如下, acquire:1 waitTime:0.0acquire:2 waitTime:0.997729acquire:3 waitTime:1.998076acquire:4 waitTime:3.000303acquire:5 waitTime:4.000223第一次获取一个令牌时,期待0s立刻可获取到(这里之所以不须要期待是因为令牌桶的预生产个性),第二次获取两个令牌,等待时间1s,这个1s就是后面获取一个令牌时因为预生产没有期待延到这次来期待的工夫,这次获取两个又是预生产,所以下一次获取(取3个时)就要期待这次预生产须要的2s了,依此类推。可见预生产不须要期待的工夫都由下一次来买单,以保障肯定的均匀解决速率(上例为1s一次)。 RateLimiter有两种实现: SmoothBursty: 令牌的生成速度恒定。应用 RateLimiter.create(double permitsPerSecond) 创立的是 SmoothBursty 实例。SmoothWarmingUp:令牌的生成速度继续晋升,直到达到一个稳固的值。WarmingUp,顾名思义就是有一个热身的过程。应用 RateLimiter.create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) 时创立就是 SmoothWarmingUp 实例,其中 warmupPeriod 就是热身达到稳固速度的工夫。类构造如下 ...

July 22, 2020 · 3 min · jiezi

基于Redis分布式锁的正确打开方式

分布式锁是在分布式环境下(多个JVM过程)管制多个客户端对某一资源的同步拜访的一种实现,与之绝对应的是线程锁,线程锁管制的是同一个JVM过程内多个线程之间的同步。分布式锁的个别实现办法是在应用服务器之外通过一个共享的存储服务器存储锁资源,同一时刻只有一个客户端能占有锁资源来实现。通常有基于Zookeeper,Redis,或数据库三种实现模式。本文介绍基于Redis的实现计划。 要求基于Redis实现分布式锁须要满足如下几点要求: 在分布式集群中,被分布式锁管制的办法或代码段同一时刻只能被一个客户端下面的一个线程执行,也就是互斥锁信息须要设置过期工夫,防止一个线程长期占有(比方在做解锁操作前异样退出)而导致死锁加锁与解锁必须统一,谁加的锁,就由谁来解(或过期超时),一个客户端不能解开另一个客户端加的锁加锁与解锁的过程必须保障原子性实现1. 加锁实现基于Redis的分布式锁加锁操作个别应用 SETNX 命令,其含意是“将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 曾经存在,则 SETNX 不做任何动作”。在 Spring Boot 中,能够应用 StringRedisTemplate 来实现,如下,一行代码即可实现加锁过程。(下列代码给出两种调用模式——立刻返回加锁后果与给定超时工夫获取加锁后果) /** * 尝试获取锁(立刻返回) * @param key 锁的redis key * @param value 锁的value * @param expire 过期工夫/秒 * @return 是否获取胜利 */public boolean lock(String key, String value, long expire) { return stringRedisTemplate.opsForValue().setIfAbsent(key, value, expire, TimeUnit.SECONDS);}/** * 尝试获取锁,并至少期待timeout时长 * * @param key 锁的redis key * @param value 锁的value * @param expire 过期工夫/秒 * @param timeout 超时时长 * @param unit 工夫单位 * @return 是否获取胜利 */public boolean lock(String key, String value, long expire, long timeout, TimeUnit unit) { long waitMillis = unit.toMillis(timeout); long waitAlready = 0; while (!stringRedisTemplate.opsForValue().setIfAbsent(key, value, expire, TimeUnit.SECONDS) && waitAlready < waitMillis) { try { Thread.sleep(waitMillisPer); } catch (InterruptedException e) { log.error("Interrupted when trying to get a lock. key: {}", key, e); } waitAlready += waitMillisPer; } if (waitAlready < waitMillis) { return true; } log.warn("<====== lock {} failed after waiting for {} ms", key, waitAlready); return false;}上述实现如何满足后面提到的几点要求: ...

July 16, 2020 · 3 min · jiezi

rabbitMQ的安装

对 rabbitMQ 我们已经有了初步的了解,现在我们来安装 rabbitMQ 来进行一些操作。因为大部分人的操作系统都是windows 而且作者本人使用的也windows系统。所以这里只介绍在windows上安装rabbitMQ。mac用户自行解决(仇富脸)。 erlang的安装erlang 不好的地方是它不是向下兼容的,也就是说 rabbitMQ的版本和erlang的版本不匹配的话,会安装失败。所以我们要先上 rabbitMQ的官方网站查询对应的版本号,再安装 网站:https://www.rabbitmq.com/whic... 查询好版本后向erlang 官方网站下载安装程序,网址:http://www.erlang.org/downloads 下载安装完成之后,配置erlang的环境变量(参考JAVA_HOME)。 变量名:ERLANG_HOME变量值:你的安装路径然后将 %ERLANG_HOME%\bin 加入到path中,和Java maven 这些程序的配置方式一样。然后在cmd 中输入 erlang 验证一下,完成。 rabbitMQ的安装下载地址:http://www.rabbitmq.com/downl... 注意要找对版本下载安装。安装完成后进入RabbitMQ的sbin目录下在cmd中执行 ./rabbitmq-plugins enable rabbitmq_management这个指令是安装 rabbitmq_management 插件。安装完成后cmd中执行(sbin目录下): ./rabbitmqctl status 可以看到rabbitMQ的一些信息,就说明rabbitMQ安装成功了。如果没有成功。检查一下版本和环境变量等信息,重新安装。 rabbitMQ的配置安装完成之后执行 sbin 下的 rabbitmq-server.bat 启动 rabbitMQ 访问 http://localhost:15672 。我们会看到一个登录界面。用户名和密码都是guest。登录进去后能看到一些交换机 队列 用户 等的信息。 图 1:rabbit的界面 guest这个用户是只能本地访问rabbitMQ的,相当去 mysql 的 root 用户。下面我们配置一个可以远程使用的开发账号。 创建用户指令: rabbitmqctl.bat add_user [username] [password]## 示例rabbitmqctl set_user_tag test test查看用户列表: rabbitmqctl list_users给用户设置权限(tag) rabbitmqctl set_user_tag [tag1] [tag2]## 示例rabbitmqctl set_user_tag test administrator rabbitMQ 有五个tag(权限) 分别是: ...

June 22, 2020 · 1 min · jiezi