关于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

如何在backoffice里创建Hybris-image-container以及分配给product

登录backoffice,在media container视图点击新建按钮: Catalog选择Product Catalog: 在Properties界面,可以选择media实例放入该container: 同步到online catalog: 同步之后,就可以把这个media container分配给product了: 在product的Administration标签页,Gallery images字段里分配media container: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

November 4, 2019 · 1 min · jiezi

hybris-backoffice创建product遇到的synchronization问题和解答

我从product DSC-H20_MD clone了一个新的product,code为DSC-H20_MD1 因为它的状态有个红灯: 所以我点了这个sync按钮: 结果报这个错: 之后这个clone出来的product就无法从backoffice里搜索到了。请教一下这种情况该如何解决呢? 后台这个clone出来的product也无法从product表里读取出来了: Jerry请教了兄弟团队的Hybris专家Kevin,得到了解答: 因为我直接clone的online版本,而stage版本里是没有这个product的,同步的意思是以stage为基准去更新online,所以我一同步就会把online里的删掉。所以我选择从一个stage版本的product clone就好了。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

November 4, 2019 · 1 min · jiezi

如何在Hybris-commerce里创建一个media对象

进入backoffice的Media中心,首先新建一个文件夹,用于存放即将创建的media对象:取名为jerryimage: 然后创建一个新的media对象,取名jerryproductimage:上传图片: 选择这个media对象存放的文件夹: 从staged catalog同步到online catalog: 同步成功:要获取更多Jerry的原创文章,请关注公众号"汪子熙":

November 4, 2019 · 1 min · jiezi

hybris-commerce-storefront的产品搜索功能

在Hybris Commerce Cloud的storefront的搜索栏键入一些字母,每次键入,会触发一个发送到后台的http请求实现live search的功能: http url如下:https://&lt;host>/electronics/en/search/autocomplete/SearchBox?term=DSC-H20_MD 注意我们键入的是产品名称,而非产品code,后者只能从Chrome开发者工具的http response里观察到: 这里能清楚地看到,DSC-H20_MD是产品的名称。 从backoffice也能清楚看到product code和name这两个字段的区分: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

November 4, 2019 · 1 min · jiezi

Hybris做增强的两种方式In-App-Extension和Side-by-Side-Extension

传统的扩展方式,即In-App增强方式,Hybris开发顾问通过Extensions的方式进行二次开发,生成的Custom Extensions同Hybris标准的Extensions一起参加构建,构建结束后新功能方可使用。在构架过程中,Hybris实例暂时无法访问(down time)。这种方式允许Hybris顾问以较高的灵活度在Custom Extensions里编写代码来实现增强需求。<img width="657" alt="clipboard" src="https://user-images.githubuse...;> 借助SAP Cloud Platform Extension Factory实现的Side-by-side增强,不需要修改Hybris实例(图二虽然标注的是Commerce Cloud,但对Hybris Commerce On-Premises版本仍然适用)本身的代码,而只需在Extension Factory上编写针对Hybris标准程序发布事件的响应处理函数。例如客户需求是当Hybris Storefront上有新用户注册,或者新订单生成时,实现一段自定义逻辑——这类事件驱动的增强需求,采用Extension Factory增强,开发效率较In-App增强更高,实现更轻量,但前提是Hybris标准应用在需要被增强的业务流程上对事件发布有完善的支持。<img width="791" alt="clipboard2" src="https://user-images.githubuse...;>

November 4, 2019 · 1 min · jiezi

成为高级程序员不得不了解的并发

到目前为止,你学到的都是顺序编程,顺序编程的概念就是某一时刻只有一个任务在执行,顺序编程固然能够解决很多问题,但是对于某种任务,如果能够并发的执行程序中重要的部分就显得尤为重要,同时也可以极大提高程序运行效率,享受并发为你带来的便利。但是,熟练掌握并发编程理论和技术,对于只会CRUD的你来说是一种和你刚学面向对象一样的一种飞跃。 正如你所看到的,当并行的任务彼此干涉时,实际的并发问题就会接踵而至。而且并发问题不是很难复现,在你实际的测试过程中往往会忽略它们,因为故障是偶尔发生的,这也是我们研究它们的必要条件:如果你对并发问题置之不理,那么你最终会承受它给你带来的损害。 并发的多面性更快的执行速度问题听起来很简单,如果你想让一个程序运行的更快一些,那么可以将其切成多个分片,在单独的处理器上运行各自的分片:前提是这些任务彼此之间没有联系。 注意:速度的提高是以多核处理器而不是芯片的形式出现的。如果你有一台多处理器的机器,那么你就可以在这些处理器之间分布多个任务,从而极大的提高吞吐量。但是,并发通常是提高在单处理器上的程序的性能。在单处理上的性能开销要比多处理器上的性能开销大很多,因为这其中增加了线程切换(从一个线程切换到另外一个线程)的重要依据。表面上看,将程序的所有部分当作单个的任务运行好像是开销更小一点,节省了线程切换的时间。 改进代码的设计在单CPU机器上使用多任务的程序在任意时刻仍旧只在执行一项工作,你肉眼观察到控制台的输出好像是这些线程在同时工作,这不过是CPU的障眼法罢了,CPU为每个任务都提供了不固定的时间切片。Java 的线程机制是抢占式的,也就是说,你必须编写某种让步语句才会让线程进行切换,切换给其他线程。 基本的线程机制并发编程使我们可以将程序划分成多个分离的,独立运行的任务。通过使用多线程机制,这些独立任务中的每一项任务都由执行线程来驱动。一个线程就是进程中的一个单一的顺序控制流。因此,单个进程可以拥有多个并发执行的任务,但是你的程序看起来每个任务都有自己的CPU一样。其底层是切分CPU时间,通常你不需要考虑它。 定义任务线程可以驱动任务,因此你需要一种描述任务的方式,这可以由 Runnable 接口来提供,要想定义任务,只需要实现 Runnable 接口,并在run 方法中实现你的逻辑即可。 public class TestThread implements Runnable{ public static int i = 0; @Override public void run() { System.out.println("start thread..." + i); i++; System.out.println("end thread ..." + i); } public static void main(String[] args) { for(int i = 0;i < 5;i++){ TestThread testThread = new TestThread(); testThread.run(); } }}任务 run 方法会有某种形式的循环,使得任务一直运行下去直到不再需要,所以要设定 run 方法的跳出条件(有一种选择是从 run 中直接返回,下面会说到。) ...

October 17, 2019 · 5 min · jiezi

设计模式单例模式

单例模式 (Singleton Pattern)使用的比较多,比如我们的 controller 和 service 都是单例的,但是其和标准的单例模式是有区别的。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 <!--more--> 模式结构单例模式的结构很简单,只涉及到一个单例类,这个单例类的构造方法是私有的,该类自身定义了一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。 源码导读单例模式分为懒汉单例和饿汉单例;饿汉单例代码很简单,顾名思义,饿汉单例就是类初始化的时候就将该单例创建,示例代码如下: public class Singleton { private static final Singleton singleton = new Singleton(); //限制产生多个对象 private Singleton(){ } //通过该方法获得实例对象 public static Singleton getSingleton(){ return singleton; } //类中其他方法,尽量是 static public static void doSomething(){ }}但是懒汉单例就不那么简单了,懒汉单例是在访问这个类的实例的时候先判断这个类的实例是否创建好了,如果没创建好就要先创建这个单例。也就是说懒汉单例是第一次访问的的时候创建单例,而不是初始化阶段。这将会导致一个问题,如果在多线程场景下,多个线程同时访问这个单例都发现其未被创建,那么这些线程就会分别创建实例,那么这个单例模式就不那么单例了——实例被多次创建。在阿里开发手册中有两条就是和懒汉单例相关的,告诉我们要如何去避免这种情况,第六节的第一条 和第十二条: (六)并发处理 1.【强制】获取单例对象需要保证线程安全,其中的方法也要保证线程安全。 说明:资源驱动类、工具类、单例工厂类都需要注意。 【推荐】在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优化问题隐患(可参考 The "Double-Checked Locking is Broken" Declaration),推荐解 决方案中较为简单一种(适用于 JDK5 及以上版本),将目标属性声明为 volatile 型。 反例: class Singleton { private Helper helper = null; public Helper getHelper() { if (helper == null) synchronized(this) { if (helper == null) helper = new Helper(); } return helper; } // other methods and fields... } volatile 关键字的作用和双重检查锁在我以往的博客中介绍过,文章地址https://mp.weixin.qq.com/s/r52hmD71TtiJjlOzQUvRlA 这篇博客介绍了并发的一些知识,小伙伴有空可以读一读。在这里 volatile 关键字的作用就是保证数据的可见性,双重检查锁是提高代码性能。下面我们分析一下手册中的反例: ...

October 17, 2019 · 1 min · jiezi

项目日志

19.10.16 多参数查询mybatis 实现参数查询关键点在于参数和sql语句 注意like、limit的用法 在这里参数用一个Map传到mybatis Mapper文件 在mapper 文件的sql书写时 传参时 出现一个问题 因为要用到 if判断 但是传过去空值时无法正确判断true或false,所以在传参之前判断是否为空值 传参就传 true或false 单参数 <select id="getXxxxXxx" parameterType="Map" resultType="Integer"> select count(*) from xxxx_xx where if(#{xxx_xxxx_xxxx},1,xxx_xxxx_xxxx like #{xxx_xxxx_xxxx}) limit #{offset},#{pageSize}</select>多参数的话直接在下面加and if 待解决问题 [ ] log4j配置问题 , 控制台不能输出log日志

October 17, 2019 · 1 min · jiezi

看完你就明白的锁系列之锁的状态

前面两篇文章我介绍了一下 看完你就应该能明白的悲观锁和乐观锁看完你就明白的锁系列之自旋锁看完你就会知道,线程如果锁住了某个资源,致使其他线程无法访问的这种锁被称为悲观锁,相反,线程不锁住资源的锁被称为乐观锁,而自旋锁是基于 CAS 机制实现的,CAS又是乐观锁的一种实现,那么对于锁来说,多个线程同步访问某个资源的流程细节是否一样呢?换句话说,在多线程同步访问某个资源时,锁的状态会如何变化呢?本篇文章来探讨一下。 锁状态的分类 Java 语言专门针对 synchronized 关键字设置了四种状态,它们分别是:无锁、偏向锁、轻量级锁和重量级锁,但是在了解这些锁之前还需要先了解一下 Java 对象头和 Monitor。 Java 对象头我们知道 synchronized 是悲观锁,在操作同步之前需要给资源加锁,这把锁就是对象头里面的,而Java 对象头又是什么呢?我们以 Hotspot 虚拟机为例,Hopspot 对象头主要包括两部分数据:Mark Word(标记字段) 和 Klass Pointer(类型指针)。 Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。这些信息都是与对象自身定义无关的数据,所以Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。 Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 在32位虚拟机和64位虚拟机的 Mark Word 所占用的字节大小不一样,32位虚拟机的 Mark Word 和 Klass Pointer 分别占用 32bits 的字节,而 64位虚拟机的 Mark Word 和 Klass Pointer 占用了64bits 的字节,下面我们以 32位虚拟机为例,来看一下其 Mark Word 的字节具体是如何分配的 用中文翻译过来就是 无状态也就是无锁的时候,对象头开辟 25bit 的空间用来存储对象的 hashcode ,4bit 用于存放分代年龄,1bit 用来存放是否偏向锁的标识位,2bit 用来存放锁标识位为01偏向锁 中划分更细,还是开辟25bit 的空间,其中23bit 用来存放线程ID,2bit 用来存放 epoch,4bit 存放分代年龄,1bit 存放是否偏向锁标识, 0表示无锁,1表示偏向锁,锁的标识位还是01轻量级锁中直接开辟 30bit 的空间存放指向栈中锁记录的指针,2bit 存放锁的标志位,其标志位为00重量级锁中和轻量级锁一样,30bit 的空间用来存放指向重量级锁的指针,2bit 存放锁的标识位,为11GC标记开辟30bit 的内存空间却没有占用,2bit 空间存放锁标志位为11。其中无锁和偏向锁的锁标志位都是01,只是在前面的1bit区分了这是无锁状态还是偏向锁状态。 ...

October 16, 2019 · 2 min · jiezi

看完你就明白的锁系列之自旋锁

看完你就明白的锁系列之自旋锁在上一篇文章 看完你就应该能明白的悲观锁和乐观锁 中我们已经学习到了什么是悲观锁和乐观锁、悲观锁和乐观锁的实现、优缺点分别是什么。其中乐观锁的实现之一 CAS 算法中提到了一个自旋锁的概念,为了全面理解 CAS 算法就首先需要了解一下自旋锁 是什么,自旋锁的适用场景和优缺点分别是什么,别着急,下面为你一一列举。 自旋锁的提出背景由于在多处理器环境中某些资源的有限性,有时需要互斥访问(mutual exclusion),这时候就需要引入锁的概念,只有获取了锁的线程才能够对资源进行访问,由于多线程的核心是CPU的时间分片,所以同一时刻只能有一个线程获取到锁。那么就面临一个问题,那么没有获取到锁的线程应该怎么办? 通常有两种处理方式:一种是没有获取到锁的线程就一直循环等待判断该资源是否已经释放锁,这种锁叫做自旋锁,它不用将线程阻塞起来(NON-BLOCKING);还有一种处理方式就是把自己阻塞起来,等待重新调度请求,这种叫做互斥锁。 什么是自旋锁自旋锁的定义:当一个线程尝试去获取某一把锁的时候,如果这个锁此时已经被别人获取(占用),那么此线程就无法获取到这把锁,该线程将会等待,间隔一段时间后会再次尝试获取。这种采用循环加锁 -> 等待的机制被称为自旋锁(spinlock)。 自旋锁的原理自旋锁的原理比较简单,如果持有锁的线程能在短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞状态,它们只需要等一等(自旋),等到持有锁的线程释放锁之后即可获取,这样就避免了用户进程和内核切换的消耗。 因为自旋锁避免了操作系统进程调度和线程切换,所以自旋锁通常适用在时间比较短的情况下。由于这个原因,操作系统的内核经常使用自旋锁。但是,如果长时间上锁的话,自旋锁会非常耗费性能,它阻止了其他线程的运行和调度。线程持有锁的时间越长,则持有该锁的线程将被 OS(Operating System) 调度程序中断的风险越大。如果发生中断情况,那么其他线程将保持旋转状态(反复尝试获取锁),而持有该锁的线程并不打算释放锁,这样导致的是结果是无限期推迟,直到持有锁的线程可以完成并释放它为止。 解决上面这种情况一个很好的方式是给自旋锁设定一个自旋时间,等时间一到立即释放自旋锁。自旋锁的目的是占着CPU资源不进行释放,等到获取锁立即进行处理。但是如何去选择自旋时间呢?如果自旋执行时间太长,会有大量的线程处于自旋状态占用 CPU 资源,进而会影响整体系统的性能。因此自旋的周期选的额外重要!JDK在1.6 引入了适应性自旋锁,适应性自旋锁意味着自旋时间不是固定的了,而是由前一次在同一个锁上的自旋时间以及锁拥有的状态来决定,基本认为一个线程上下文切换的时间是最佳的一个时间。 自旋锁的优缺点自旋锁尽可能的减少线程的阻塞,这对于锁的竞争不激烈,且占用锁时间非常短的代码块来说性能能大幅度的提升,因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗,这些操作会导致线程发生两次上下文切换! 但是如果锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块,这时候就不适合使用自旋锁了,因为自旋锁在获取锁前一直都是占用 cpu 做无用功,占着 XX 不 XX,同时有大量线程在竞争一个锁,会导致获取锁的时间很长,线程自旋的消耗大于线程阻塞挂起操作的消耗,其它需要 cpu 的线程又不能获取到 cpu,造成 cpu 的浪费。所以这种情况下我们要关闭自旋锁。 自旋锁的实现下面我们用Java 代码来实现一个简单的自旋锁 public class SpinLockTest { private AtomicBoolean available = new AtomicBoolean(false); public void lock(){ // 循环检测尝试获取锁 while (!tryLock()){ // doSomething... } } public boolean tryLock(){ // 尝试获取锁,成功返回true,失败返回false return available.compareAndSet(false,true); } public void unLock(){ if(!available.compareAndSet(true,false)){ throw new RuntimeException("释放锁失败"); } }}这种简单的自旋锁有一个问题:无法保证多线程竞争的公平性。对于上面的SpinlockTest,当多个线程想要获取锁时,谁最先将available设为false谁就能最先获得锁,这可能会造成某些线程一直都未获取到锁造成线程饥饿。就像我们下课后蜂拥的跑向食堂,下班后蜂拥地挤向地铁,通常我们会采取排队的方式解决这样的问题,类似地,我们把这种锁叫排队自旋锁(QueuedSpinlock)。计算机科学家们使用了各种方式来实现排队自旋锁,如TicketLock,MCSLock,CLHLock。接下来我们分别对这几种锁做个大致的介绍。 ...

October 15, 2019 · 3 min · jiezi

设计模式笔记大纲

title: 设计模式笔记-大纲date: 2019-04-25 09:49:37tags: 设计模式 作者:muggle 设计模式的分类创建型模式共五种: 工厂方法模式抽象工厂模式单例模式建造者模式原型模式结构型模式共七种: 适配器模式装饰器模式代理模式外观模式桥接模式组合模式享元模式行为型模式共十一种: 策略模式模板方法模式观察者模式迭代子模式责任链模式命令模式备忘录模式状态模式访问者模式中介者模式解释器模式设计原则设计模式的最终目的是为了实现代码设计的六大基本原则的,我们在使用设计模式的时候千万要记住这一点,不用为了使用设计模式而去强行套设计模式 单一职责原则不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不然,就应该把类拆分。 当需求变化时,将通过更改职责相关的类来体现。如果一个类拥有多于一个的职责,则多个职责耦合在一起,会有多于一个原因来导致这个类发生变化。一个职责的变化可能会影响到其他的职责,另外,把多个职责耦合在一起,影响复用性。 单一职责原则解决的问题: 降低类的复杂度;提高类的可读性,提高系统的可维护性;降低变更引起的风险(降低对其他功能的影响)。里氏替换原则任何基类可以出现的地方,子类一定可以出现。 只有当子类可以替换掉父类, 代码功能不受到影响时,父类才能真正被复用, 而子类也能够在父类的基础上增加新的行为;从而达到代码复用与扩展的目的;里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。 里氏替换原则解决的问题: 增强程序的健壮性, 版本升级时也可以保持非常好的兼容性。提高代码复用率依赖倒置原则这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。 开闭原则对于引用的模块尽量去扩展,而不是去修改它,也就是所谓的对扩展开放对修改关闭。开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则,抽象化是开闭原则的关键。 接口隔离原则接口隔离原则(Interface Segregation Principle, ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。 根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可 迪米特原则一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现、如何复杂都与调用者或者依赖者没关系,调用者或者依赖者只需要知道他需要的方法即可,其他的我一概不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。 设计模式解析首先允许我提一点建议,有不少设计模式的教程喜欢拿生活中一些事物举例子,比如什么什么奥迪汽车,宝马,汽车工厂这种。这种方式确实能让人更加直观的的体会到一个设计模式的用法和作用,但是我以前这样学的后果就是——看啥都像设计模式,想往设计模式里面套又套不出个所以然来。实际上有的设计模式结构是由好几个角色组成,该如何去写怎么命名都是确定的有理有据的,你光记住生活中的一个例子然后平时写代码去套这个例子,你会有种无从下手的感觉。我们应该把每个设计模式的使用场景结构都记住,但是这样死记难记不说,还很难做到活学活用。我的的建议是结合源码去记,这样你能根据源码中的例子依瓢画葫芦写出自己想要的设计模式出来,我接下来对设计模式的讲解也是结合源码进行讲解的。 然后,我个人建议是能不用设计模式的地方就不用设计模式。并不是因为设计模式不好,一个设计得好的设计模式确实可以减少我们工作量和代码维护成本,但是一个设计不好的设计模式使用起来代价巨大。要知道设计模式的最终目的是减少我们的工作量,不要为了设计模式而设计模式。一个垃圾的设计模式的缺点:代码维护成本翻倍,因为会凭空多了好多莫名其妙的类;代码读不懂,你设计模式用的乱七八糟,别人只会感觉你的代码毫无逻辑,绕来绕去。 一个好的设计模式,首先要用对场景,然后命名要规范,要让别人一看命名就知道你用的什么设计模式,然后根据设计模式去找相关联的类,这样别人脑海里才能形成一个脉络,有点没法按设计模式规范命名的地方也请加上注释,不然让别人猜你的代码写了些啥,用的啥设计模式吗?

October 15, 2019 · 1 min · jiezi

设计模式日记组合模式

组合(Composite)模式的定义:有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系。组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码; 模式结构顶层抽象:树枝或者树叶的抽象接口树枝:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。树叶:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件源码导读组合模式分为透明模式和安全模式;透明模式是在顶层抽象中声明了所有管理子对象的方法,树叶节点点和树枝节点对于客户端来说没有区别。安全模式是在顶层抽象中只声明叶子和树枝公有的抽象方法,而将对叶子和树枝的管理方法实现到对应的类中,因此客户端就需要区分该节点是树枝还是叶子从而调用对应的方法。 对组合模式来说,List Set等这些集合类属于不那么严格的组合模式。由于没有找到太好的源码,因此我在这里分别对透明模式和安全模式组合说明 透明模式: public abstract class Component{ private String name; public Component(string name) { this.name = name; } public abstract boolean Add(Component component); public abstract boolean Remove(Component component); public String getName(){ return name; }}public class Branch extend Component{ private List<Component> tree=new ArrayList<>(); public Branch(String name){ super(name); } public boolean add(Componet component){ tree.add(component); return true; } public boolean Remove(Component component){ tree.remove(component); return true; }}public class Leaf extend Component{ public Leaf(String name){ super(name); } public boolean add(Componet component){ return false; } public boolean Remove(Component component){ return false; } }安全模式: ...

October 15, 2019 · 1 min · jiezi

设计模式Factory三个工厂模式

简单工厂模式简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 <!--more--> 模式结构简单工厂模式包含如下角色: Factory:工厂角色,工厂角色负责实现创建所有实例的内部逻辑Product:抽象产品角色,抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口ConcreteProduct:具体产品角色,具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。源码导读 public static void main(String[] args) { // 资源加载 ClassPathResource classPathResource = new ClassPathResource("spring-bean.xml"); // XmlBeanFactory 加载资源并解析注册bean BeanFactory beanFactory = new XmlBeanFactory(classPathResource); // BeanFactory.getBean(); UserBean userBean = (UserBean) beanFactory.getBean("userBean"); System.out.println(userBean.getName());}这个XmlBeanFactory便可以看做是一个稍微变形的简单工厂,getBean()方法便是获取产品的实例方法,userBean便是我们的产品。如果我们以后遇到与spring中XmlBeanFactory类似场景我们便可依瓢画葫芦写出一个漂亮的简单工厂。 工厂方法模式也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。 模式结构工厂方法模式包含如下角色: Product:抽象产品ConcreteProduct:具体产品Factory:抽象工厂ConcreteFactory:具体工厂源码导读java.util.Collection接口中定义了一个抽象的iterator()方法,该方法就是一个工厂方法。我们来看看ArrayList中的iterator()实现 @NotNull public Iterator<E> iterator() { return new ArrayList.Itr();}它new了一个ArrayList的内部类Itr 然后将其返回,Itr: private class Itr implements Iterator<E> { int cursor; int lastRet = -1; int expectedModCount; Itr() { this.expectedModCount = ArrayList.this.modCount; } ...... ......}这里ArrayList对Iterator来说就是一个工厂类,它的iterator()方法便是生产Iterator的工厂方法。 ...

October 15, 2019 · 1 min · jiezi

高并发

什么是高并发高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。高并发相关常用的一些指标有 响应时间(Response Time),吞吐量(Throughput),每秒查询率 QPS(Query Per Second),并发用户数 等。响应时间: 系统对请求做出响应的时间。例如系统处理一个 HTTP 请求需要 200ms,这个 200ms 就是系统的响应时间。吞吐量: 单位时间(年,月,日,时,分,秒)内处理的请求数量。QPS: 每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。并发用户数: 同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。如何提升系统的并发能力互联网分布式架构设计,提高系统并发能力的方式,方法论上主要有两种:垂直扩展(Scale Up) 与 水平扩展(Scale Out)。 垂直扩展提升单机处理能力。垂直扩展的方式又有两种: 增强单机硬件性能,例如:增加 CPU 核数如 32 核,升级更好的网卡如万兆,升级更好的硬盘如 SSD,扩充硬盘容量如 2T,扩充系统内存如 128G;提升单机架构性能,例如:使用 Cache 来减少 IO 次数,使用异步来增加单服务吞吐量,使用无锁数据结构来减少响应时间;在互联网业务发展非常迅猛的早期,如果预算不是问题,强烈建议使用 “增强单机硬件性能” 的方式提升系统并发能力,因为这个阶段,公司的战略往往是发展业务抢时间,而 “增强单机硬件性能” 往往是最快的方法。 不管是提升单机硬件性能,还是提升单机架构性能,都有一个致命的不足:单机性能总是有极限的。所以互联网分布式架构设计高并发终极解决方案还是水平扩展。 水平扩展只要增加服务器数量,就能线性扩充系统性能。水平扩展对系统架构设计是有要求的,如何在架构各层进行可水平扩展的设计,以及互联网公司架构各层常见的水平扩展实践,是本文重点讨论的内容。

October 5, 2019 · 1 min · jiezi

CAP定理与BASE理论

CAP定理与BASE理论CAP定理2000 年 7 月,加州大学伯克利分校的 Eric Brewer 教授在 ACM PODC 会议上提出 CAP 猜想。2年后,麻省理工学院的 Seth Gilbert 和 Nancy Lynch 从理论上证明了 CAP。之后,CAP 理论正式成为分布式计算领域的公认定理。CAP 理论为:一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。 一致性(Consistency): 一致性指 (all nodes see the same data at the same time),即更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致。可用性(Availability): 可用性指(Reads and writes always succeed),即服务一直可用,而且是正常响应时间。分区容错性(Partition tolerance): 分区容错性指(the system continues to operate despite arbitrary message loss or failure of part of the system),即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。CAP 权衡通过 CAP 理论,我们知道无法同时满足一致性、可用性和分区容错性这三个特性,那要舍弃哪个呢?对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,所以节点故障、网络故障是常态,而且要保证服务可用性达到 N 个 9,即保证 P 和 A,舍弃C(退而求其次保证最终一致性)。虽然某些地方会影响客户体验,但没达到造成用户流程的严重程度。 对于涉及到钱财这样不能有一丝让步的场景,C 必须保证。网络发生故障宁可停止服务,这是保证 CA,舍弃 P。貌似这几年国内银行业发生了不下 10 起事故,但影响面不大,报道也不多,广大群众知道的少。还有一种是保证 CP,舍弃 A。例如网络故障是只读不写。 ...

October 5, 2019 · 1 min · jiezi

微服务

概述优点第一,它解决了复杂问题。它把可能会变得庞大的单体应用程序分解成一套服务。虽然功能数量不变,但是应用程序已经被分解成可管理的块或者服务。每个服务都有一个明确定义边界的方式,如远程过程调用(RPC)驱动或消息驱动 API。微服务架构模式强制一定程度的模块化,实际上,使用单体代码来实现是极其困难的。因此,使用微服务架构模式,个体服务能被更快地开发,并更容易理解与维护。第二,这种架构使得每个服务都可以由一个团队独立专注开发。开发者可以自由选择任何符合服务 API 契约的技术。当然,更多的组织是希望通过技术选型限制来避免完全混乱的状态。然而,这种自由意味着开发人员不再有可能在这种自由的新项目开始时使用过时的技术。当编写一个新服务时,他们可以选择当前的技术。此外,由于服务较小,使用当前技术重写旧服务将变得更加可行。 第三,微服务架构模式可以实现每个微服务独立部署。开发人员根本不需要去协调部署本地变更到服务。这些变更一经测试即可立即部署。比如,UI 团队可以执行 A|B 测试,并快速迭代 UI 变更。微服务架构模式使得持续部署成为可能。 最后,微服务架构模式使得每个服务能够独立扩展。您可以仅部署满足每个服务的容量和可用性约束的实例数目。此外,您可以使用与服务资源要求最匹配的硬件。例如,您可以在 EC2 Compute Optimized 实例上部署一个 CPU 密集型图像处理服务,并且在 EC2 Memory-optimized 实例上部署一个内存数据库服务。 缺点微服务另一个主要缺点是由于微服务是一个分布式系统,其使得整体变得复杂。开发者需要选择和实现基于消息或者 RPC的进程间通信机制。此外,由于目标请求可能很慢或者不可用,他们必须要编写代码来处理局部故障。虽然这些并不是很复杂、高深,但模块间通过语言级方法/过程调用相互调用,这比单体应用要复杂得多。微服务的另一个挑战是分区数据库架构。更新多个业务实体的业务事务是相当普遍的。这些事务在单体应用中的实现显得微不足道,因为单体只存在一个单独的数据库。在基于微服务的应用程序中,您需要更新不同服务所用的数据库。通常不会选择分布式事务,不仅仅是因为 CAP 定理。他们根本不支持如今高度可扩展的 NoSQL 数据库和消息代理。您最后不得不使用基于最终一致性的方法,这对于开发人员来说更具挑战性。 测试微服务应用程序复杂。例如,使用现代框架如 Spring Boot,只需要编写一个测试类来启动一个单体 web 应用程序并测试其 REST API。相比之下,一个类似的测试类对于微服务来说需要启动该服务及其所依赖的所有服务,或者至少为这些服务配置存根。再次声明,虽然这不是一件高深的事情,但不要低估了这样做的复杂性。 微服务架构模式的另一个主要挑战是实现了跨越多服务变更。例如,我们假设您正在实现一个变更服务 A、服务 B 和 服务 C 的需求,其中 A 依赖于 B,且 B 依赖于 C。在单体应用程序中,您可以简单地修改相应的模块、整合变更并一次性部署他们。相反,在微服务中您需要仔细规划和协调出现的变更至每个服务。例如,您需要更新服务 C,然后更新服务 B,最后更新服务 A。幸运的是,大多数变更只会影响一个服务,需要协调的多服务变更相对较少。 部署基于微服务的应用程序也是相当复杂的。一个单体应用可以很容易地部署到基于传统负载均衡器的一组相同服务器上。每个应用程序实例都配置有基础设施服务的位置(主机和端口),比如数据库和消息代理。相比之下,微服务应用程序通常由大量的服务组成。例如,据 Adrian Cockcroft 了解到,Hailo 拥有 160 个不同的服务,Netflix 拥有的服务超过 600 个。 每个服务都有多个运行时实例。还有更多的移动部件需要配置、部署、扩展和监控。此外,您还需要实现服务发现机制,使得服务能够发现需要与之通信的任何其他服务的位置(主机和端口)。比较传统麻烦的基于票据(ticket-based)和手动的操作方式无法扩展到如此复杂程度。因此,要成功部署微服务应用程序,需要求开发人员能高度控制部署方式和高度自动化。 一种自动化方式是使用现成的平台即服务(PaaS),如 Cloud Foundry。PaaS 为开发人员提供了一种简单的方式来部署和管理他们的微服务。它让开发人员避开了诸如采购和配置 IT 资源等烦恼。同时,配置 PaaS 的系统人员和网络专业人员可以确保达到最佳实践以落实公司策略。 ...

October 4, 2019 · 1 min · jiezi

spring-statemachine的企业可用级开发指南2先跑起来

上一篇说了很多废话,这一篇就不唠叨,先跑起来 1、来个spring boot去start.spring.io新建一个springboot的项目,虽然我对spirngboot也有不少的牢骚,但作为demo的开始,还是一个很好用的脚手架,记得选spring statemachine,为了方便,我还选了web 模块 点击generate project 下载到本地,用IDE打开,顺便说一句,我用的是java IDE界逼格很低的eclipse,因为我一直用它,还不要钱。 2、跑起来一个废物例子 在本地打开后我们首先看pom.xml文件,里面和我们相关的有这几段<properties> <spring-statemachine.version>2.0.1.RELEASE</spring-statemachine.version></properties><dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-starter</artifactId></dependency><dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-bom</artifactId> <version>${spring-statemachine.version}</version> <type>pom</type> <scope>import</scope></dependency> 现在就可以在springboot里面用statemachine了,然后我们就开始想办法跑起来。 先来一个StateMachineConfig,它的主要作用就告诉状态机的初始状态应该啥样,然后把整个状态流程都用代码配置出来。@Configuration是springboot的注解,表示这个类负责配置,@EnableStateMachine表示这个配置类是用在spring statemachine上面的。package com.skyblue.statemachine.config;import java.util.EnumSet;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.annotation.Configuration;import org.springframework.statemachine.config.EnableStateMachine;import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;@Configuration@EnableStateMachinepublic class StateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception { states.withStates().initial(OrderStates.UNPAID).states(EnumSet.allOf(OrderStates.class)); } @Override public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception { transitions.withExternal().source(OrderStates.UNPAID).target(OrderStates.WAITING_FOR_RECEIVE).event(OrderEvents.PAY).and() .withExternal().source(OrderStates.WAITING_FOR_RECEIVE).target(OrderStates.DONE).event(OrderEvents.RECEIVE); }}它配套需要OrderStates和OrderEvents,代码如下: ...

June 12, 2019 · 2 min · jiezi

这一次我连-webxml-都不要了纯-Java-搭建-SSM-环境

在 Spring Boot 项目中,正常来说是不存在 XML 配置,这是因为 Spring Boot 不推荐使用 XML ,注意,并非不支持,Spring Boot 推荐开发者使用 Java 配置来搭建框架,Spring Boot 中,大量的自动化配置都是通过 Java 配置来实现的,这一套实现方案,我们也可以自己做,即自己也可以使用纯 Java 来搭建一个 SSM 环境,即在项目中,不存在任何 XML 配置,包括 web.xml 。 环境要求: 使用纯 Java 来搭建 SSM 环境,要求 Tomcat 的版本必须在 7 以上。快速体验1 创建工程创建一个普通的 Maven 工程(注意,这里可以不必创建 Web 工程),并添加 SpringMVC 的依赖,同时,这里环境的搭建需要用到 Servlet ,所以我们还需要引入 Servlet 的依赖(一定不能使用低版本的 Servlet),最终的 pom.xml 文件如下: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.6.RELEASE</version></dependency><dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope></dependency>2 添加 Spring 配置工程创建成功之后,首先添加 Spring 的配置文件,如下: @Configuration@ComponentScan(basePackages = "org.javaboy", useDefaultFilters = true, excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})public class SpringConfig {}关于这个配置,我说如下几点: ...

May 28, 2019 · 3 min · jiezi

JavaWeb 乱码问题终极解决方案!

经常有读者在公众号上问 JavaWeb 乱码的问题,昨天又有一个小伙伴问及此事,其实这个问题很简单,但是想要说清楚却并不容易,因为每个人乱码的原因都不一样,给每位小伙伴都把乱码的原因讲一遍也挺费时间的,因此,松哥今天决定写一篇文章,和大伙好好捋捋 JavaWeb 中的乱码问题。 对于一些老司机而言,其实并不太容易遇到乱码问题,但是对于一些新手来说,乱码几乎是家常便饭,而且每当乱码时,网上搜了一大堆解决方案,发现自己的问题还是没能解决,其实这就是平时研究代码不求甚解导致的,乱码问题,也要去分析,然后才能对症下药,才能药到病除。整体思路首先出现乱码之后,要先去确认乱码的地方,当一个网页上出现乱码,有可能是浏览器显示问题,也有可能是 Java 编码问题,也有可能数据库中的数据本身就是乱码的,所以我们要做的第一件事就是确认乱码发生的位置,缩小 bug 范围,通过打印日志或者 debug 首先去确认乱码发生的位置,然后再去进一步解决,一般来说,乱码的原因大致上可以分为两类:请求乱码响应乱码请求乱码,可能是因为参数放在 URL 地址中乱码,也有可能是参数放在请求体中乱码,不同传参方案也对应了不同的乱码解决方案。如果是响应乱码,那么原因就会比较多了,一般来说,有如下几种可能的原因:数据库本身乱码数据在 Java 代码中乱码数据在浏览器显示的时候乱码数据在从 Java 应用传到数据库的过程中乱码对于不同的乱码原因,会有不同的解决方案,对症下药,才能药到病除,所以当出现乱码时,大家要做的第一件事就是分析乱码发生的原因,找到原因了,才能找到解决方案。基本原则发生乱码是因为各自编码不同导致的,所以,大家首先要有一个良好的开发习惯,项目编码,文件编码都要统一起来,松哥有个同事就因为 Freemarker 乱码,找了半天没找到原因,后来在松哥建议下修改了项目编码,乱码问题才解决了,一般来说,公司制度稍微成熟一些,都会对项目编码,文件编码有硬性规定的。在Eclipse 中,设置项目编码方式如下(工程的编码要提前设置,如果项目已经开发一半再去设置,已有的中文就会乱码): Window->Preferences->General 然后对于 JSP 文件也需要提前设置好编码方式,如下: 这是在 Eclipse 中设置文件编码,如果是在 IntelliJ IDEA中,则不需要设置JSP文件编码,因为默认就是 UTF-8,只需要提前设置下工程编码即可: 除了开发工具的编码,数据库的编码也要统一,一般来说,主要是设置一下数据库的编码和数据表的编码,如下: 设置数据库编码:CREATE DATABASE vhr DEFAULT CHARACTER SET utf8;设置数据表编码:DROP TABLE IF EXISTS adjustsalary;CREATE TABLE adjustsalary ( id int(11) NOT NULL AUTO_INCREMENT, eid int(11) DEFAULT NULL, PRIMARY KEY (id),) ENGINE=InnoDB DEFAULT CHARSET=utf8;这些是准备工作,这些工作做好了,还是有可能会遇到乱码问题,接下来我们就具体问题具体分析。请求乱码请求乱码,就是说数据在浏览器中显示是正常的,但是传到 Java 后端之后,就乱码了,这种乱码一般来说,分为两种:参数放在 URL 地址中导致的乱码参数放在请求体中导致的乱码两种乱码原因,对应了两种不同的解决方案。分别来看。URL 地址中的参数乱码这种乱码主要发生在 GET 请求中,因为在 GET 请求中我们一般通过 URL 来传递参数,这个问题可以在代码中解决,但是太过于麻烦,因此一般我们直接在Tomcat配置中解决,修改 Tomcat的conf/server.xml 文件,修改 URL 编码格式,如下: 这样就可以搞定 URL 地址中的参数乱码。请求体中的参数乱码请求体中的参数乱码,我们可以在解析参数之前通过设置 HttpServletRequest 的编码来解决,如下:request.setCharacterEncoding(“UTF-8”);但是一样也太过于麻烦,所以如果是普通的 Servlet/JSP 项目,我们就可以直接定义一个过滤器来处理,如下:public class EncodingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(“UTF-8”); chain.doFilter(request, response); }}过滤器配置: <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.sang.filter.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/</url-pattern> </filter-mapping>在工程编码和JSP/HTML编码都没问题的情况下,请求乱码基本上就是这两种情况。响应乱码如果在浏览器上加载页面看到了乱码,大家首先要确认在从服务端往浏览器写数据的前一刻,这个数据还没有乱码(即数据库中查询出来的数据是OK的,没有发生乱码的问题),那么对于这种乱码,我们只需要设置响应数据的 ContentType 就可以了,如下:response.setContentType(“text/html;charset=UTF-8”);如果从数据库中查询出来的数据就是乱码的,那么就需要去确认数据库中的编码是否 OK 。框架处理前面提到的方案,都是在 Servlet/JSP 项目中我们可以采用的方案,在 SSM 框架中当然也可以使用,但是,SpringMVC 框架本身也提供了一个过滤器,我们可以借用这个过滤器更加高效的解决响应乱码问题,如下:<filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param></filter><filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/</url-pattern></filter-mapping>当然,上面这段配置并不能代替 Tomcat 中 conf/server.xml 中的编码配置,如果是在 Spring Boot 中,配置可以更加简单,只需要在 application.properties 中添加如下配置即可:server.tomcat.uri-encoding=UTF-8spring.http.encoding.force-request=truespring.http.encoding.force-response=true其他乱码其他乱码主要是指使用一些第三方框架导致的乱码,例如使用 Alibaba 的 fastjson,开发者就需要在配置 HttpMessageConverter 时指定编码格式,否则就有可能出现乱码,这种第三方框架的乱码松哥没法穷举,大伙在使用时需要注意看官方文档,fastjson 的 HttpMessageConverter 配置如下:@BeanFastJsonHttpMessageConverter fastJsonHttpMessageConverter() { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig config = new FastJsonConfig(); config.setCharset(Charset.forName(“UTF-8”)); converter.setFastJsonConfig(config); converter.setDefaultCharset(Charset.forName(“UTF-8”)); return converter;}一个隐蔽的乱码除了前面介绍的这几种乱码之外,还有一个比较隐蔽的乱码,容易被很多初学者忽略的地方,就是数据在从 Java 应用传递到 MySQL 的过程中,发生了乱码,这种问题一般在 Windows 上不易发生,如果数据库装在 Linux 上,则这个问题就很容易发生,数据在代码中命名没有乱码,存到 MySQL 上就乱码了,但是如果直接使用 Navicat 等工具往 MySQL 上存储数据,又不会乱码,或者 MySQL 中数据没有乱码,但是用 Java 查询出来就乱码了,这种都是数据在 应用 和 数据库 之间传递时发生了乱码,解决方式很简单,在数据库连接地址上指定编码即可,如下:db.url=jdbc:mysql:///yuetong?useUnicode=true&characterEncoding=UTF-8大致就这些,还有一些非常偶尔的情况可能会用到 @RequestMapping 注解中的 produces 属性,在这里指定数据类型即可。 好了,差不多就这些,下次有人问你为啥我的又乱码了,直接把这篇文章甩给他。大伙有什么解决乱码的独门密器也可以一起来讨论。 关注松哥,公众号内回复 牧码小子,获取松哥私藏多年的Java干货哦! ...

April 9, 2019 · 1 min · jiezi

Spring Boot 2 - 初识与新工程的创建

Spring Boot的由来相信大家都听说过Spring框架。Spring从诞生到现在一直是流行的J2EE开发框架。随着Spring的发展,它的功能越来越强大,随之而来的缺点也越来越明显,以至于发展到后来变得越来越臃肿,使用起来也非常的麻烦。到后来由于过于强调配置的灵活性,有时即使只为了加入一个简单的特性,而需要相当多的XML配置,从而被人们诟病为"配置地狱"!后来许多优秀的服务端框架涌现出来,比如基于JavaScript的nodeJS,基于Python的Django,Flask,Tornado框架。都由于其使用简单的特性被越来越多的开发者采用。Sprint Boot就是为了应对这些框架的挑战而出现的,它彻底改变了Spring框架臃肿的现状。使得J2EE的框架变得简单起来,目前越来越多的公司和项目选择了它。Spring Boot最新的版本是2.x,本文我们就来介绍它的安装与配置,快速创建你的第一个Spring Boot工程,享受她的优雅与强大。Spring Boot的特性Spring Boot的主要有以下几个杀手级特性,可以大大减少学习与使用的复杂性,让我们更多地关注业务,提升开发效率:可创建独立可运行的应用程序,打包后仅一个jar包,运行即可。内置应用服务器Tomcat,Jetty等,无需部署。零XML配置,彻底摆脱"配置地狱"。自动配置各种第三方库,常用的第三方库引入即可用。内置各种服务监控系统,实时观察服务运行状态。创建Spring Boot工程我们废话不多说,现在就开始介绍创建Spring Boot 2工程的方法,这是进行Spring Boot学习与开发的第一步。方法一:通过Idea内置工具创建如果你使用IntelliJ IDEA作为你的开发IDE的话,这种方式最为方便,不过前提是使用Ultimate版(最终版),在IntelliJ的官网可以下载到(当然如果条件允许推荐购买正版)。打开Idea选择创建新工程选择导航栏中的Spring Initializr然后填入工程信息注意这里有使用Maven还是Gradle的选择。我们这里既然要零XML配置,这里选择使用Gradle工程,如图。我们使用Sprint Boot的目的也就是简化我们的开发生活,不是吗?添加第三方依赖我们这里添加需要的第三方依赖。如果你第一次接触Spring Boot,为了避免复杂性,可以选择添加以下两个依赖。其他的依赖不必担心,你可以在任何时候非常容易地添加依赖。DevTools:是一系列开发工具配置,比如热部署。Web: 对Web开发的基础支持。完成工程创建填入工程名和保存目录后,点击完成。创建完工程后,会有一个gradle配置的一个界面,这里我们选择使用默认的wrapper。这个选项会自动为我们下载对应版本的gradle进行配置和编译,无需我们自己安装配置等,非常方便。点击OK后我们就成功地创建了新工程!恭喜!方法二:通过Spring Initializr创建这种方式适用于不使用IntelliJ IDEA和使用免费版Idea的同学,通过官方创建Spring Boot工程的网站直接创建。方法一其实也是使用这个网站作为模板来集成到Idea中的。点击这里进入到这个网站(https://start.spring.io/)输入工程信息,并选择Gradle工程输入工程的信息后,如果需要更详细的信息设置,可以点击下方的"More options"按钮进行设置。添加依赖这里我们可以直接搜索需要的依赖进行添加,比如我们添加Web和Devtools库。生成工程在我们把所有信息填完后,接下来我们就可以点击页面底部的按钮(Generate Project)开始生成。生成后会自动把工程下载到本地,我们解压后,将该工程保存到开发目录(你喜欢的任何位置都可以),然后使用IDE打开即可。比如我这里使用的是IntelliJ IDEA,打开即可。运行工程!至此我们的工程已经创建完毕,下面就是运行它了。我们观察工程源码包的结构,发现有一个Hellospringboot2Application的类,这个类就是我们服务的运行入口。运行它后,我们的服务就可以正常启动了!总结通过创建Spring Boot新工程的过程,我们就会发现它的简洁之处,不会像以前使用Spring那样要花费很多时间和精力去创建和配置,我们现在甚至可以在短短的两分钟之内创建好工程!后面的文章我们会深入讨论Spring Boot的方方面面。我的博客中其他关于Spring Boot的所有文章可以点击这里找到,欢迎关注!如果有问题可以留言,或者给我发邮件lloyd@examplecode.cn,期待我们共同学习与成长!

March 19, 2019 · 1 min · jiezi