花括号MC

其他

设计模式系列——建造者模式

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。建造者模式用于将复杂对象的创建和表示分离,有些对象由很多部分组成,每个部分又可以有多种不同选择,创建这种对象的时候往往需要考虑使用建造者模式。举个例子一辆汽车由发动机,方向盘,车灯,车灯,车身颜色等组成,每辆车的颜色,车轮大小,车灯样式可能会不一样,但是车的组成部分不会少。建造模式有两种实现方式,第一种方式是有导演的方式,第二种是无导演方式。根据我的经验日常使用无导演的方式可能会更多一些。有导演所谓有导演就是通过一个导演类来指挥对象创建的过程,客户端使用导演类来获取对象,不用关心对象具体的创建过程。先看一下UML图,对建造模式有个大概的了解。看一下具体代码,我们以建造一辆汽车举例public
2021年10月25日
其他

设计模式系列——工厂模式

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。工厂模式属于创建型设计模式。是日常使用比较多的一种设计模式。比如Logback,Spring等框架中都大量使用该设计模式。上篇文章提到的简单工厂模式虽然在简单系统可以用一用,但是因为工厂类本身包含太多业务逻辑,而且如果想增加新的图表类型,需要修改工厂类等缺点。所以我们真正生产系统更多的使用工厂模式。工厂模式为每一个类都定义一个单独的工厂类,该工厂类只负责创建单一的产品。举个例子通过工厂模式实现日志的创建,为不同种类的Logger分别创建不同类型的工厂。来看一下UML图:来看一下代码如何实现先来定义一个Logger接口,以及LoggerFactory接口public
2021年9月17日
其他

设计模式系列——简单工厂模式

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。简单工厂严格的说并不是GOF里面提到的标准的23个设计模式之一。但是日常开发中经常会用到,特别是比较小的系统。简单工厂模式就是通过一个工厂类的工厂方法创建相应的对象。举个例子我们希望创建三种不同类型的图表,并进行数据展示,这个时候可以考虑使用简单工厂模式。先来看一下UML图:来看一下代码如何实现。先来定义一个抽象类
2021年9月14日
其他

程序员应该掌握的常用网络问题定位工具

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。项目日常运维的过程中,经常会遇到各种奇奇怪怪的网络问题。那么排查网络问题,就成为一个合格的程序员必备技能。这里列举出一些常用的指令,用于日常工作中快速定位网络问题。ping这个是大家经常用到的一个小工具,用于检查两台服务器之间是否能够成功交换数据包。ping指令向对方主机发送ICMP报文。当能成功ping通时表示两台主机之间的网络链路是畅通的。如果ping不通,首先需要确认对方是否关停了ping服务,如果没有关停,就需要检查双方的网络链接问题了。telnet该指令主要用于检查到对方端口的网络联通性,如果telnet能通,一般证明TCP三次握手已经建立成功,也就是网络层是畅通的。如果telnet不通,需要确认对方是否已经启动相应服务端口,如果已启动,那么就要检查双方的防火墙策略等问题。ifconfigifconfig
2021年4月27日
其他

当我们设置IP地址为0.0.0.0时,是在表达什么

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。什么是IP地址简单说IP地址是由32位二进制数组成的一串编码。但是因为32位二进制不好记忆,我们经常会用点分十进制方式来表示IP地址,也就是每八位二进制为一组,将其转换成成十进制,中间用点隔开。比如
2021年4月23日
其他

Nginx:不管正向还是反向我通通代理

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。关于Nginx,除了用它做正常的Web服务器,还经常用它做网络代理服务器使用;来解决网络访问受限的问题。日常大家讨论最多的就是“正向代理”和"反向代理"。很多人,经常会弄不明白什么是"正向代理",什么又是"反向代理",他们之间到底有什么区别?其实Nginx只有一种代理转发功能。之所以大家经常会说正向代理或者是反向代理是因为站的角度不一样。我们可以想象一下80-90年代,机关大院里面负责收发信件的老大爷,把老大爷收发信件动作,想象成Nginx对请求的转发功能。当老大爷把内部信件发往外部时,我们定义为正向代理,那么当老大爷把外部发往内部的信件转发给内部每个具体的人时,就叫做反向代理。但是对于老大爷来说都是代理,完成的动作都是一样的。对于Nginx来说也是一样的,所有网络请求转发指令都是一样的。Nginx代理指令对比将内部请求转发到外部。upstream
2021年4月20日
其他

该如何理解Java中的Class类

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。java里面一切都是类和对象当我们在程序中要表达书这个实体时,往往会定义一个Book类。例如public
2021年4月8日
其他

不会使用虚拟机的程序员不是好程序员

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。相信每个程序员都会有使用虚拟机的需求,因为日常工作学习中,很多问题都需要在集群环境下进行,比如Mysql集群,Redis集群,Hadoop集群,Spark集群,K8S集群等等。没有虚拟机的帮忙,钱包还真受不了,毕竟租云主机来进行日常学习不是谁都能承担的起的。常见的虚拟软件有Vmware和Virtual
2021年2月26日
其他

聊聊Web Api 认证方案那点儿事

Token的有效期,直至达到一个最大有效期。有人问为什么不通过重新Login来获取新的Token呢,因为重新Login需要用户输入用户名和密码,对用户体验不好,而使用Refresh
2021年1月14日
其他

聊聊对称加密与非对称加密

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。目前大家常说的信息加密技术就两种,对称加密和非对称加密。对称加密所谓的对称加密就是加密和解密用同一份密钥。对称加密的好处就是加密速度快,但是缺点也很明显,一定要保存好这份密钥,如果密钥丢失,就会带来很大的安全风险。而且如果与服务端进行通信的客户端比较多的话,服务端要管理很多份不同的密钥。非对称加密为了解决对称加密的缺点,人们提出了非对称加密,非对称加密技术也是目前应用最广泛的加密技术。所谓的非对称加密就是生成一对密钥,分为公钥和私钥。私钥自己保存,公钥发布出去。用私钥加密的信息只能用公钥解密,用公钥加密的信息也只能用私钥解密。举个例子假设你的好朋友铁蛋,经常网购。购物网站为了保证信息安全传输,决定生成一对密钥,私钥自己保存,公钥发给铁蛋。铁蛋发给购物网站的信息都是用公钥加密的信息,购物网站收到后,用自己的私钥进行解密。这样即使传输途中,信息被截获,也没办法破解,因为没有私钥。有个漏洞这个时候有个黑客,做了一个假的购物网站,页面和真的网站一摸一样,连网址都很相似。同时也生成了一对密钥,最重要的是黑客偷偷的把铁蛋电脑里真购物网站的公钥给替换成了假购物网站的公钥。当铁蛋打开假的网址进行购物操作时,会用假的公钥进行加密然后把消息发给假的网站,整个通信过程,铁蛋都没有发现自己被骗。铁蛋之所以被骗,是因为他不知道电脑里的真公钥已经被换成假的了。为了解决这个问题,出现一个认证机构,这个机构会对网站的公钥进行加密签名并形成一个证书,这个证书就是大家常说的CA证书,这个机构就是CA。只要铁蛋电脑里面安装的是经过CA认证的证书,在访问的时候,浏览器就会显示出一把小锁,提示可以安全访问,如果没有经过认证,浏览器就会提示铁蛋有风险。铁蛋经常在多个网站购物,难道需要每个网站都保存一份证书?其实不用这样,我们的电脑里面一般都有CA的根证书,只要有了这个证书,所有经过CA认证过的网站就都是安全的。没错,我上面介绍的就是HTTPS的工作原理,这也是非对称加密技术的典型应用。双向认证非对称加密还有一个应用场景就是双向认证。所谓的双向认证就是不仅服务端要验证客户端的身份,客户端也要验证服务端的身份。说白了就是客户端和服务端各自生成一个密钥对,私钥自己保存,公钥发给对方。这种情况一般用在系统与系统的对接上。为什么需要双向认证假设C系统要访问S系统的服务,S系统对外提供的数据安全级别比较高,只有受信系统才能访问。假设目前只允许C系统访问。S系统为了保证信息安全传输,决定生成一对密钥,私钥自己保存,公钥发给C系统。C系统访问S的时候,会用S的公钥将消息进行加密发给系统S,S用私钥进行解密。如果这个时候窃密者B系统偷偷拿到的S提供给C的公钥,然后B用S的公钥加密消息之后发送给系统S,获取相应的数据,那么S是没办法判断出请求的消息是来自C还是来自窃密者B。如何保证消息来自于受信系统C呢?经过协商,C系统也生成一对密钥,私钥自己保存,公钥发给系统S。当C向S发送请求的时候,C先将要发的消息,假设为M,进行一次Hash运算,得到一个固定长度的Hash值,这个值一般称为数字摘要。然后C用自己的私钥对这个数字摘要进行加密,加密后的值称为数字签名,其实跟现实中我们在文件中进行手写签名是一个意思。C系统将数字签名和要发送的消息M,一起用S的公钥做一次加密之后发送给系统S。S收到消息之后,先用自己的私钥对消息进行解密,解密之后,得到C的数字签名和消息M。这个时候关键的一步来了,就是验签,验证消息是否是C发来的。首先S用C的公钥对签名进行解密操作,解密完成后会得到一个摘要值,我们称之为D。能解开就证明消息是C发来的。S还会用和C一样的Hash算法对消息M进行Hash运算,这样将会得到一个摘要值,我们称之为D‘,如果D和D’相同就证明消息没有被篡改过。总结上面的双向认证过程也是有漏洞的,只不过相比于单项认证,降低了被破译的风险而已。注意,世界上不存在绝对安全的系统,即便是现在逐渐流行起来的生物识别系统。我们所做的各种安全措施都只是在降低系统被破解的风险而已。就像家里的防盗门,只防君子不防小人。推荐阅读1.
2020年8月2日
其他

一篇小文带你走进RabbitMQ的世界

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。说到消息中间件,大部分人的第一印象可能是Kafka。毕竟Kafka自问世以来,就顶着高并发,大流量的光环。当然了Kafka也不负众望,在大数据处理方面独领风骚。这里想说说另一款同样优秀的消息中间件RabbitMQ。选RabbitMQ还是Kafka如果单机数据量没有到十万级以上,我觉得选哪个都OK,如果超过百万甚至到了千万级别那么建议选择Kafka。对了还有重要的一点,RabbitMQ支持事务,而Kafka不支持。所以如果你的业务系统要求支持事务,那么只能选RabbitMQ。这也是很多金融系统选择RabbitMQ作为消息中间件的原因。RabbitMQ基本概念先来说一下消息中间件的通用模型,所有的消息中间件的模型基本都是这样。而RabbitMQ是基于AMQP协议实现的。模型大概是这个样子,如下图所示重点关注中间两个框框,下面依次解读一下。信道建立TCP链接是一件很费时的事情,所以很多提供高并发服务的软件都支持TCP链接复用,比如HTTP协议的KeepAlive就是为了复用TCP链接准备的。所以RabbitMQ提出了信道的概念,一个TCP链接里面可以支持多个信道同时通信,以提高通信效率。如下图所示。broker一个启动的RabbitMQ实例,代表一个broker。虚拟主机虚拟主机可以理解成命名空间的概念,方便队列管理。交换器向RabbitMQ发送消息时,先把消息先发到交换器,再由交换器根据相关路由规则发到特定队列上。目前
2020年7月14日
其他

在高可用这条路上你知道Redis有多努力吗

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。自我介绍我,Redis,内存数据库,有着比memcached更强大的功能。现在已经是这个领域的头把金交椅。常规数据库这里所说的常规数据库是指基于硬盘读写的数据库,比如Oracle,Mysql,Mongodb等。基于硬盘读写的数据库可以有效的保证数据的高可用性。这里的高可用性指的是操作系统或者数据库崩溃之后,不会造成数据丢失,这也是对数据库的最基本要求。内存数据库基于硬盘读写的数据库虽然可以保证数据的高可用性,但是读写速度比较慢,这也是磁盘I/O的天然属性。虽然切换固态硬盘之后,性能会有显著提升,但是经济成本也会随之升高,而且固态硬盘使用寿命偏低。为了解决这个问题,Redis做出了改变。Redis是基于内存进行读写的数据库,把数据全部存储在内存中,这样就可以大幅提高数据的读写速度。memcached说到内存数据库,不得不提memcached,memcached比Redis出现的更早,也是基于内存进行数据存储。在十几年前,大家通用的缓存方案就是memcached。memcached支持Key-Value形式的数据存储,但是只支持String类型的数据结构,不支持更复杂的数据结构,也不支持集群。操作系统或者memcached重启后数据就会丢失。这也是基于内存进行数据存储的最大缺点。RedisRedis继承了memcached所有的优点,并改进了很多缺点。比如Redis也是基于内存进行数据操作,并且支持更多的数据类型,比如List,Set等。最最主要的,也是这篇文章的重点,Redis支持数据高可用,也就是说Redis或者操作系统重启之后,数据不会丢失。Redis在高可用这条路上所付出的努力,就像一个不断努力进取的励志青年。单机持久化众所周知,放在内存中的数据是不稳定的。为了解决因为系统或者Redis重启造成的数据丢失问题。Redis提供了两种数据持久化方案。快照备份把Redis数据库中的数据,定时备份到磁盘中。当数据库重启的时候,可以通过定时备份到磁盘文件中的快照文件进行数据恢复。这样Redis既保证了数据的读写速度,又保证了数据的高可用。AOF同步写快照备份有个缺点,就是会丢失一部分数据。比如在新的快照文件生成之前,系统发生了问题,那么最近一次快照之后的数据将丢失。Redis为了解决这个问题,提出了AOF解决方案。所谓的AOF就是将每次写入数据的命令都以追加的方式记录到文件中。这样在系统出问题的时候,只要将这个文件中的命令全部重放一下就OK了。这样就可以做到不丢数了。但是如果数据写入操作太多的话,会造成AOF文件过大,为了解决这个问题Redis提供了AOF自动压缩功能,以及去重功能,这样可以达到对文件体积大小进行优化的目的。主从复制上面的两种持久化方案,对于单节点Redis来说,基本已经够用了。但是我们的系统总是越做越大,要求越来越多。有的时候单节点Redis往往撑不住系统的访问量。这种情况下Redis提供了主从模式。所谓的主从模式就是一个主节点,负责读和写,一个从节点,负责将主节点的数据同步到从节点,这样主从节点信息就是一致的。注意:从节点不支持写操作,但是可以支持读操作。当其中任意一个点挂掉之后,数据不会损失。而且可以将读的压力分散到多个节点,支持更大的访问量。哨兵模式对于主从模式,这里有个最大的痛点。当主节点挂掉后,从节点是不会自动升级为主节点的。也就是负责往Redis写入的程序会报错,但是读操作不会有问题。这一点不太符合高可用的要求。为了解决发生故障,主节点自动切换的问题,Redis又给大家提供了哨兵模式。所谓的哨兵模式就是,提供三个哨兵节点(同样是Redis实例,只不过不存储数据),来监控主从模式下的所有Redis节点(真正存储数据的节点)。客户端程序通过哨兵节点获取主节点信息。当主节点挂掉后,哨兵节点会自动将其中一个从节点升级为主节点,提供给客户端程序执行写入操作。当发生故障的主节点恢复后,会自动变为新的主节点的从节点。集群模式大家可能发现了,无论是主从复制模式,还是哨兵模式都没有解决分布式写的问题,也就是说到目前为止,所有的方案都只能往一个节点写数据,数据存储能力受单节点限制。哨兵模式仅仅解决了主从复制模式下,发生故障后不能自动切换的问题。为了解决分布式写的问题,Redis提供了集群功能。Redis集群可以实现分布式写。集群中的节点分为主节点和从节点。主节点负责数据的读写以及集群信息的维护,从节点负责同步主节点的信息。Redis集群利用数据分片的概念,将要操作的Key进行哈希计算,根据得到的结果决定这个Key应该存储到那个主节点。这样就可以利用多个主节点进行分布式写操作。进行读操作的时候也会先计算Key的哈希值,然后找到对应的主节点。很遗憾的是,集群模式也不是百分百完美,比如key的批量操作会受限制,只有当操作的key都位于一个槽位时才能进行操作。还有Keys操作,只能在任一节点发生,不能跨节点。其实这些所有缺点,都是因为分布式写造成的,因为你把数据分别存到了不同的Redis节点。总结Redis由单节点的持久化,到主从复制模式,再到哨兵模式,再到最后的集群模式。一路打怪升级,不断的完善自己。推荐阅读1.
2020年6月24日
其他

Java并发编程那些事儿(十)——最后的总结

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第十篇文章,也是最后一篇文章。这篇文章将会对之前的文章内容做一个简单的总结和梳理。要学习并发编程,首先要了解什么是线程,什么是任务,以及线程和任务之间的关系,这部分内容在第一篇文章有提到。并发编程要解决的核心问题就是如何做到多个线程安全的读写共享变量。想做到安全的读写共享变量一共有两个方法。第一个方法是通过加锁的方式实现。第二个方法就是避免竞争,每个线程都持有一份共享变量的拷贝,也就是大家常用的ThreadLocal。这两种方法,分别在第二篇和第三篇有详细的介绍。并发编程除了要解决多线程安全读写共享变量的问题,还要解决线程间互相通信的问题。因为有些任务,线程间必须互相配合才能更快速的完成任务,比如一个线程负责洗碗,一个线程负责烘干,那么烘干任务必须在洗碗线程结束之后才能开始。Java里面有两种实现线程间通信的方法,分别是基于内置锁的wait()和notify()方法,以及基于显示锁的await()和signal()方法。这部分内容的详细介绍可以看我的第四篇文章。上面所有的内容都是如何保证同一时刻只有一个线程在工作,实际工作中我们往往希望,某一个时刻有固定数量的线程在工作,比如用户并发量,这个时候我们可以通过使用Semaphore来实现。这是Java提供的内置工具类,类似的工具类还有CountdownLatch等。具体的介绍可以看第五篇文章。线程的创建和销毁是一件很耗费系统资源的事情,而且线程不能无限创建,因为每个线程都会占用大概1M的内存空间,为了保证我们更快的使用资源,实际项目中,都会采用线程池的方式使用线程。Java提供了Executor框架让我们方便的使用线程池,同时Executor还成功的将线程和任务进行了解耦。总之实际编程过程中都会使用Executor框架。具体如何使用可以参考第六篇文章。知道如何创建并启动线程很重要,但是知道如何安全的终止线程更加重要。就好比如何让飞机安全平稳的落地比知道飞机如何起飞更重要一样。第七篇文章详细介绍了线程终止的内容。死锁,并发编程最希望避免的问题,但是你好像总是踩在坑里。第八篇文章介绍了死锁相关问题。希望能帮你避坑。并发编程的所有问题,归根结底都会回归本质,那就是JVM内存模型。所以了解JVM,了解JVM内存模型才是关键。在第九篇文章中介绍了内存相关的知识。最后附上所有文章的链接:Java并发编程那些事儿(一)
2020年6月15日
其他

Java并发编程那些事儿(九)——内存模型

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第九篇文章。上一篇介绍了多线程死锁问题,这一篇说一下Java的内存模型,很多并发问题都是由内存模型决定。还记得第三篇文章中提到的有关volatile的例子吗?
2020年6月11日
其他

Java并发编程那些事儿(八)——死锁

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第八篇文章。上一篇介绍了任务的取消及关闭,这一篇说一下死锁的问题。哲学家问题一提到死锁,很多人都会想到哲学家问题。假设有5个哲学家,围坐在一张圆桌旁,哲学家只做两件事,吃饭和思考。吃饭的时候需要使用两只筷子,但是每个哲学家面前只放了一支筷子,如果想吃饭,必须借用旁边哲学家的筷子。这个时候如何管理每个哲学家吃饭与思考的时机和顺序就变得很重要。如果每个哲学家,拿起自己面前的筷子的时候,发现旁边的筷子不可用时,并不是释放手里筷子,而是死等旁边的筷子,就会发生死锁。所有哲学家都会因为等待对方的筷子而饿死。死锁抽象模型多个线程相互持有彼此正在等待的锁而又不释放自己已经持有的锁时就会发生死锁现象。对于上面的哲学家问题,筷子就是锁,而每位哲学家就是一个线程。如果非要对死锁的场景做个简单的分类的话,那么大概可以分为如下几类。锁顺序死锁如果一个线程需要持有两把锁才能干活。比如线程A先拿到1号锁,然后再拿2号锁,而这个时候线程B抢先持有了2号锁,而等待持有1号锁。这个时候将造成死锁,造成这种死锁的原因是因为两个线程获取锁的顺序错乱了,如果都是按照先拿1号锁,在拿2号锁的顺序执行,就不会发生死锁情况。资源死锁假设一个任务需要对两个数据库进行连接,两个数据库的连接对象都从对应的数据库连接池中获取。当线程A持有数据库1的连接,等待数据库2的连接,而线程B持有数据库2的连接,而等待数据库1的链接,如果任意一个数据库链接池资源不足,那么也将发生死锁现象。线程饥饿还有一种情况也会发生线程阻塞,假设我们定义了只有一个线程的线程池,但是我们提交了2个任务,第一个任务依赖于第二任务,但是因为第二个任务进入了等待队列(只有一个线程执行任务)。所以整个程序将会被阻塞住。如何避免死锁通过上面对死锁问题的产生原因进行分析,我们大概可以想到有那么几种方式可以避免死锁。第一种方式就是避免持有多个锁,但是这种情况只适合比较简单的业务场景。第二种方式就是使用带有超时时间的显示锁Lock,在规定的时间内获取不到相应的锁资源时则自动释放已经持有的锁资源,这样就可以避免长期持有锁而形成死锁。结束死锁问题很难被发现,即便你经历了严格的测试。死锁问题往往发生在线上高并发的场景下。所以各位在写并发程序的时候,一定要仔细分析业务需求及自己实现的代码逻辑。推荐阅读1.
2020年6月9日
其他

Java并发编程那些事儿(七)——取消及关闭

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第七篇文章。上一篇介绍了Executor框架和线程池相关的内容,这一篇说一下任务的取消及关闭。俗话说的好,知道如何起飞很重要,但是知道如何平稳落地更重要。线程和任务的启动我们已经很清楚了,但是如何做到线程及任务安全的关闭及取消,也是有一定技巧的。取消任务大部分场景下,启动的任务都会按照计划正常的运行,然后结束。但是有些场景我们必须提前结束任务。比如用户发送了取消请求。超过了某个规定的时间。发生了某种错误。应用程序被关闭。Java里面取消任务的执行一般有两种方法。第一种方法,设置一个标志位,任务执行过程会检查该标志位,如果为true则结束任务。该方法对于执行阻塞调用的情况下会有缺陷,下面会详细说第二种方法,通过中断来安全的结束任务。标志位假设有一个产生偶数的生成器,我们希望通过设置一个标志位来结束该任务,否则这个任务会一直运行下去。定义一个偶数生成任务,示例代码如下
2020年6月2日
其他

Java并发编程那些事儿(六)——Executor框架及线程池

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第六篇文章。上一篇介绍了一些比较常用的并发工具类,这篇主要说一下Executor框架及线程池。什么是线程池顾名思义,线程池就是存放线程的池子,池子里面存放的是已经创建好的N个线程;Java里面一般用List或Set等容器类来存储线程,实现线程池功能。为什么要使用线程池首先,线程的创建和销毁是很耗费时间和资源的一件事情。其次,线程不能无限制的创建,每个线程都会占用内存资源,而且如果线程过多,线程之间的调度也是一件很消耗系统性能的事情。线程池带来的好处可以做到随用随取,节省因创建线程而花费的时间通过设置线程池容量,可以保证创建的线程数量在一个合理范围区间,不会耗光系统资源。如何创建线程池Java提供了Executor框架,可以让我们简单方便的使用线程池。举个例子1//创建一个为容量为2的线程池2Executor
2020年5月28日
其他

Java并发编程那些事儿(五)——闭锁、栅栏、信号量

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第五篇文章。上一篇介绍了线程间的通信问题,这篇主要说一下JDK提供的比较常用的三个并发工具类,闭锁,栅栏,和信号量。闭锁——CountDownLatch闭锁类似于一道大门,所有的线程都在大门外等候,当大门打开时,所有线程一起开工。CountDownLatch提供了一个构造函数,可以传入一个整数作为参数,表示初始计数器。每调用一次countDown()方法时,计数器减一,当计数器减到0时,表示大门开放。可以把CountDownLatch想象成赛车的倒计时计数器,当计数器为0时,所有赛车加速驶出。下面我们通过开发一个简单的压力测试小工具来演示CountDownLatch的使用。压力测试小工具在测试一个系统的处理能力的时候,往往要求测试工具能够模拟出多个客户端同一时刻对服务端发起请求的情况,以此来判断系统的抗压能力。下面的代码借助CountDownLatch实现了该功能,并统计出最后一条请求的完成时间,以此来判断系统的最大TPS。
2020年5月23日
其他

Java并发编程那些事儿(四)——线程间的协作

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第四篇文章。上一篇介绍的是通过ThreadLocal的方式实现多线程间的共享资源的访问,这篇介绍一下线程之间如何进行通信。之前介绍的内容都是如何保证线程之间的运行互不干扰,但是有的时候,线程之间必须互相合作。比如清洗盘子完成之后,才能对盘子进行烘干操作,烘干必须在清洗之后,那么清洗线程和烘干线程如何进行沟通呢?Java进程间的通信与访问共享变量一样,都需要借助互斥的特性来实现,在互斥的基础上,JDK为线程提供了一种自我挂起的能力。也就是说想实现进程间的通信,前提必须是在synchronized同步块或者方法中实现。实现互斥同步机制,Java提供了两种方法。内置锁synchronized和显示锁Lock,所以线程间通信也有两种方式。第一种方式是基于内置锁,通过Object对象提供的wait()方法以及notify()/notifyAll()方法实现。第二种方式是基于显示锁,通过Condition对象的await()方法和signal()/signaAll()方法。内置锁——线程间通信wait():如果当前线程在等待某个条件发生变化之后,才能继续执行,但是触发条件发生变化,超出了当前线程的能力。通常需要另外一个线程来改变这种条件的时候,就需要考虑使用wait()方法,将当前线程挂起,等待条件的改变。比如想把盘子由清洗状态改为烘干状态,必须由烘干线程完成。只有烘干线程才知道烘干操作什么时候完成。当调用wait()方法将当前挂起之后,只有在notify()或者notifyAll()方法发生时,这个任务才会被唤醒,继续执行。wait()方法提供了两种使用形式,第一种接受一个时间参数,表示在此时间范围内执行挂起操作,时间到期后自动恢复。第二种就是不接受时间参数,那么将无限挂起,除非调用了notify()或者notifyAll()方法。notify()/notifyAll():表示唤醒正在因调用了wait()方法而等待的线程。notify()只唤醒一个等待线程,而notifyAll()会唤醒所有因持有同一把锁而等待的任务。wait()与sleep()的区别wait()与sleep()最大的区别在于,调用wait()方法时,会释放锁资源,而sleep()方法则不会。显示锁——线程间通信基于显示锁实现的线程间同步机制是通过Condition对象的await()方法和signal()/signalAll()方法实现的。await()方法的作用等同于wait()而signal()/signalAll()等同于notify()/notifyAll()方法。示例一通过示例代码演示一下文章开头描述的场景,一个洗碗线程,一个烘干线程,当碗是湿的时候,则挂起,同时唤醒烘干线程,反之亦然。定义Dish
2020年5月20日
其他

Java并发编程那些事儿(三)——ThreadLocal及原子性与可见性

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第三篇文章。上一篇介绍的是线程间通过锁同步的方式实现共享资源的安全访问,这篇讲一下如何通过不加锁的方式实现共享可变资源的访问。ThreadLocal介绍上篇文章讲到,如果想在多线程的环境下,实现共享可变资源的安全访问,最好的方式是加锁,也就是同一时刻只有一个线程在使用共享可变资源。如果我们有一种方式可以根除对变量的共享,那么就可以实现不加锁的情况下对变量进行安全访问。还拿之前抢卫生间坑位的例子举例,如果只有一个卫生间坑位,五个人都想去卫生间的话,那么就需要加锁同步。如果给每个人都提供一个单独的坑位,那么就可以不加锁了,因为没有争抢的场景发生。Java通过
2020年5月17日
其他

Java并发编程那些事儿(二)——锁

原创:花括号MC(微信公众号:huakuohao-mc)。关注JAVA基础编程及大数据,注重经验分享及个人成长。这是并发编程系列的第二篇文章。上一篇介绍了线程和任务的关系,以及如何创建线程。这篇说一下多线程如何正确的访问共享可变资源。所谓的共享可变资源就是每个线程都可以读也都可以写的资源。如何让多个线程正确的修改以及读取共享变量是一门学问。问题引入如下段代码实现了一个线程计数器功能,也就是统计一下有多少个线程执行了任务。首先定义一个任务public
2020年5月13日
其他

Java并发编程那些事儿(一) ——任务与线程

花括号MC(huakuohao-mc):关注JAVA基础编程及大数据,注重经验分享及个人成长。这是Java并发编程系列的第一篇文章,因为并发编程涉及的内容太多,很难用一篇或者两篇文章就说清楚,所以会分成多篇进行讲述。针对并发编程,我大概列了一份简要的提纲,后面的文章基本会按照这个顺序来写。具体能不能全部写完,我也不太清楚;主要是因为自己的能力水平有限,加之写技术文章确实很占用休息时间。简要提纲正文开始提到并发编程,很多人会想到多线程;希望让多个线程共同完成一项任务,以提高生产效率。所以要聊并发编程之前,就要先说一下什么是线程,要说什么是线程就需要说一下什么是进程。进程:在现代操作系统中,每一个独立运行的程序都是一个进程,比如运行中的word,微信等等都是一个独立进程。线程:在现代操作系统中,线程也叫轻量级进程,每个进程里面可以包含多个线程。CPU资源可以在多个线程之间不断切换,仿佛所有线程在并行执行。每个线程都有自己的计数器,堆栈,和局部变量等属性。这些线程也能够访问共享的内存变量。这将成为日后阻碍我们写出健壮且安全的并发程序的最大障碍。如何理解线程和进程上面介绍的进程和线程,对于某些人来说可能比较抽象,为了方便大家更好的理解线程和进程关系,我们可以做这样一个比喻。一个进程相当于一条运行中的汽车生产线,主要任务是生产汽车;生产线上有很多工人,每个工人负责不同的工作,有的给汽车拧螺丝,有的给车刷漆。每个工人就是一个线程。汽车就是共享变量。任务和线程的关系在Java里面,任务和线程是两个完全独立的概念,所谓的线程就是你通过
2020年5月11日
其他

Awk这件上古神兵你会用了吗

3解释awk的意思是告诉操作系统我要执行awk程序,请做好准备。单引号里面的内容为具体的程序逻辑。awk_books.log为要处理的文件。写awk的程序其实很简单,因为是有固定套路的。套路就是
2020年4月21日
其他

Java8的Stream流真香,没体验过的永远不知道

花括号MC(huakuohao-mc):关注JAVA基础编程及大数据,注重经验分享及个人成长。虽然现在Oacle官方发布的最新JDK版本已经到了JDK14。但我相信很多团队的生产系统还是JDK8,甚至有的团队还是JDK7或者JDK6。即便很多团队已经将生产环境升级为JDK8,但是代码却还是老代码,也就是说根本没有使用JDK8提供的新特性。JDK8
2020年4月14日
其他

从Jquery到Vue 一次编程思维的转换

花括号MC(huakuohao-mc):关注JAVA基础编程及大数据,注重经验分享及个人成长。已经很多年没有写前端代码了,我的前端水平还停留在Jquery一统天下的年代。最近想趁着假期,恶补一下前端的知识,于是就看了一下最近特别流行的Vue.js。这一看不要紧,发现自己已经落后了整整一个时代。前端编程思想已经由Jquery
2020年4月9日
其他

作为一个程序员,你应该知道的编码知识

花括号MC(huakuohao-mc):关注JAVA基础编程及大数据,注重经验分享及个人成长。在谍战剧里,我们经常看到这样一个桥段,特工人员,千辛万苦拿到一条信息,打开一看是一串数字,然后赶紧跑到一个秘密地方,拿出一个密码本(也可能是一本唐诗选),按照一定规则(只有自己人知道),比如第一个数字表示页数,第二个数字表示行数,第三个数字表示第几个字,逐一将信息翻译出来。如果这个过程中用了错误的密码本,或者不知道规则,那么将会解码失败。计算机的编解码过程跟上面的过程是一样一样的。计算机只认
2020年3月26日
其他

手把手教你搭建一套ELK日志搜索运维平台

花括号MC(huakuohao-mc):关注JAVA基础编程及大数据,注重经验分享及个人成长。有些事情,当我们勇敢的迈出第一步的时候,其实就已经完成了50%。很显然,学习ELK就属于这样的事情。很多对ELK不了解的同学,每当想学习的时候,就因为涉及的组件比较多,导致自己不知道如何开始,最后的结果就是永远没有开始,让自己一直处于临渊羡鱼的状态。大部分程序员学一门新语言的时候都是从“hello
2020年3月20日
其他

教你如何学习Java的 NIO

ByteBuffer.wrap("hello".getBytest())rewind():将position设置为0。clear():清空缓冲区。hasRemaining()
2020年3月9日
其他

教你如何理解JAVA的I/O类库

花括号MC(huakuohao-mc):关注JAVA基础编程及大数据,注重经验分享及个人成长。Java的I/O流,说简单也简单,说复杂也复杂。复杂是因为进行一次常规的文件I/O操作通常要用到很多I/O基础类,这很容易让新手产生困惑。简单是因为它的设计思想其实很简单。只要理解了它的设计思想就很容易知道如何使用它们。Java的I/O类库最基本的思想就是流的概念。你可以把它类比成水流,我们把数据从一个地方搬运到另一个地方就是一次I/O操作。比如读取文件
2020年2月28日
其他

这也许就是产品和开发互撕的本质原因吧

花括号MC(huakuohao-mc):关注JAVA基础编程及大数据,注重经验分享及个人成长。产品经理,不是真的经理,程序员也不是真的猿。网上一直流传着这样一个段子。“做不了程序员的,转行去做运维了,做不了运维的,改做产品了,做不了产品的,改去做运营了,做不了运营的呢,转身又去报班参加程序员培训了。”上面的内容终归是个段子,我们应该敬畏每一个行业,因为术业有专攻,更因为人情练达皆学问。但程序员跟产品经理的互撕是这个行业永远避不开的话题,每一次的互撕就像西门吹雪跟叶孤城决战紫禁之巅一样扣人心弦,杀机四起。事后便会成为大家的谈资。中国有句俗语叫做狗咬狗一嘴毛,我认为形容产品跟开发互撕最为贴切了。因为我觉得双方互撕的本质原因是双方做的都不够好,不够优秀。我们可以简单的把产品和开发分为两类,一类普通的,一类优秀的。普通的产品:我要个色彩斑斓的黑,手机壳颜色根据心情改变,总之就是异想天开,想起一出是一出,完全活在自己的世界里,毫无逻辑思维可言,更没有整体规划。优秀的产品:足够强的逻辑思维能力,对所负责的产品,上下游系统,数据流,业务模型及其了解,甚至有过数据建模,或者相关开发工作经验,也会积极了解市场及商务上的更多细节。优秀的开发:扎实的基本功,多年的项目经验,先思考,后建模,最后写代码。普通的开发:主要靠吭哧C,吭哧V,不求甚解,拿到需求之后,立刻开始写代码,不做深入思考。其实日常工作中无外乎这几类人互相组合。不同的人组合到一起,碰撞出不同的火花。所谓的优秀公司及团队只不过是优秀的人多了一些而已。这里以买鞋需求为例,给大家简单演义一下,你们感受一下。普通产品VS普通开发产品:帮我去商场买双鞋吧。开发:好嘞,你等着,马上就好。开发:鞋来了,正宗老北京布鞋,千层底,特舒服!产品:嗯...,我要的不是布鞋,我要的是运动鞋。开发:行,你等会儿,我给你换一双去。开发:鞋来了,阿迪王运动鞋,最新款。产品:我不要阿迪王,要的是阿迪达斯。开发:哎,我现在去给你换。开发:鞋来了,正宗阿迪达斯,工厂店买的。产品:嗯...,还是不对,我要的是白色的运动鞋,不要黑色的休闲鞋。开发:尼玛,那你不早说。(嘴里巴拉巴拉……,但还是去换鞋了。)产品:你也没早问啊,一个需求做了这么久还没做明白,巴拉巴拉……。开发:鞋来了,阿迪达斯,白色的,运动鞋。产品:好的,我看看,咦?好像尺码小了,而且我想要个冬款的,跑步用的运动鞋。开发:我去,你故意耍老子,就这个了,老子为了改需求已经通宵好几个晚上了,爱穿就穿,不穿拉倒!产品:不行,你必须现在去给我换,不然就去找领导。最后的画面你们自行脑补吧。普通产品VS优秀开发产品:去商场帮我买双鞋吧。开发:把需求说具体一点,不然没法实现。产品:嗯,具体点儿的需求是给我买一双运动鞋。开发:满脸黑线...,把需求可以再说具体点儿吗,这个需求还是太模糊了。产品:我已经说了,要一双运动鞋,还不够明确吗?(已经生气了,心里在想,程序员脑子都在想什么,中国话听不懂吗?)开发:你要把品牌,颜色,尺码,价位都告诉我,最好在给我发一张图片(这叫产品原型),如果商场没有同款,我该怎么办?(这叫产品的异常及错误处理方案)产品:哦,说的好像是那么回事儿,程序员脑子就是牛逼,想的周到。开发:你最好写个需求文档,然后发个邮件。看到了吗,优秀的程序员会在项目开始前,主动帮助产品把需求明确细化,这样可以避免开发阶段的无效的加班和工作。优秀产品VS普通开发产品:给我买一双,阿迪的,白色的,42码的,价位在800-1000人民币之间,跑步用的冬款运动鞋,这是是需求文档,你可以看一下。开发:好的,你等着。马上给你买回来。风一样消失在产品的视线中。过了一会儿,产品经理电话响了。开发:我在商场看到了阿迪王的鞋,你看可以吗?产品:我要的是阿迪达斯,不是阿迪王……开发:我找了好久,实在找不到,这个商场可能没有(这在技术上实现起来比较困难),我需要去另外一个商场,你等我一会儿(项目可能延期)。产品:……,独自风中凌乱。开发:我昨天加班了,找到阿迪达斯了,这里面有个春款的,你凑合穿吧(有很多bug)。项目马上deadline了,先用这个吧。产品:我天天陪你加班,最终却做出个这么个东西,完全体现不出我的想法。开发:要的需求这么刁钻,春款跟冬款有毛区别,都能跑步。看看,一个优秀的开发是多么重要,难怪一线大厂养了了那么多优秀的程序员。优秀产品VS优秀开发产品:帮我去商场买双鞋吧。阿迪的,白色的,42码的,价位在800-1000人民币之间,跑步用的冬款运动鞋,这是需求文档,你可以看一下。开发:好的,我先看一下需求文档,做一下技术评估。最迟今天下班前给你结论。产品:好的,有什么不妥之处,随时沟通。开发:我看了一下,大概有几个问题需要明确,这双鞋要穿多久,每天穿着的频率,需要防水功能吗?产品:大概穿两年左右,每周穿一次,不需要防水功能。开发:好的,我去帮你到商场选鞋。优秀的开发跟优秀的产品在产品设计阶段就已经不断的配合了,这个过程将持续到产品的整个生命周期中,这将避免很多不必要的返工跟加班。结束上面的场景虽然是我演义的,但想现实工作中也大抵如此,我写这篇文章的目的是想表达,各位应该尽量跟优秀的人一起工作,这样自己的成长速度跟工作效率才是最高的。如何才能够跟优秀的人一起工作呢,我能想到的就是让自己变得足够优秀,优秀到可以加入一个更好的团队跟平台。下一篇给各位讲一个真实的故事,一个程序员小W的成长过程。推荐阅读:一个程序员的自我修养我是如何进行架构设计的·END·
2020年1月17日
其他

Shell编程里面的奇葩字符

花括号MC(huakuohao-mc):关注JAVA基础编程及大数据,注重经验分享及个人成长。在我接触过的编程语言里面,最奇葩的两个就是JavaScript和Shell了。因为它们有着太多的语法糖跟特殊字符,总是能够让你措手不及,并且编程习惯也跟Java和Python大相径庭。总之,千万不要因为它们是脚本语言就轻视它们,否则你会被打的晕头转向。这两个奇葩之间比起来呢,Shell的奇葩之处在于特殊符号多,骚操作也多。比如你以为
2020年1月14日
其他

为什么httpd的event模块比worker更优秀

上面说了那么多I/O,估计都被我忽悠晕了,一会儿阻塞非阻塞,一会儿同步异步的。简单总结一下,阻塞I/O、非阻塞I/O、I/O复用、信号驱动都是同步I/O模型。只有异步I/O模型才是异步操作。
2020年1月1日
其他

Apache httpd 是如何实现高并发服务的

既然event和worker都是进线程混合模式,那么为什么event模块的性能要比worker模块的性能好很多呢?我将在下篇博文中详细说明。
2019年12月29日
其他

你真的了解 apache httpd 的多路复用吗

进程线程混合模式。跟worker模式很像,不同的是采用epoll模式,在2.4之前的版本是实验版本,现在已经稳定了。性能比worker要好很多。并在keepalive方面做了优化。
2019年12月24日
其他

Java程序员必读核心书单—基础版

张三丰在武当山教张无忌太极功夫时候说了一句话,你有九阳神功护体,学什么武功都会很快的。果真,张无忌很快学会了太极功夫,并且马上痛扁了玄冥二老。
2019年12月13日
其他

一个程序员的自我修养

很多年前,刚上大学时,系主任对我们讲,你们这些理科生啊,就喜欢钻牛角尖,芝麻大的小事儿,被你们搞得西瓜那么大,最后变得不可收场。而那些经管学院的学生就喜欢大事化小,小事化无。你们做事情要学会多变通。
2019年12月9日
其他

Javaer运维指令合集(快餐版)

这年头,大家都喜欢吃快餐,虽不健康,但是奈何真香!这里我把经常使用的运维指令总结成一套指令快餐。之所以称之为快餐是因为每个指令都是简单的一句或者两句话的简介。注意很多指令用法奇多,文中不会一一列出。
2019年11月27日
其他

你的内存够用吗

cache。如果判断系统内存是否够用,也可以看第五行的used值是否不断变动,如果频繁变动说明内核不断进行内存和swap的数据交换,物理内存可能不够用了。
2019年11月24日
其他

操作系统是如何管理内存的

我们先来看另外一个问题----内存超载。每个程序员都希望有一块无限大的内存,但是这不现实。就像你想拥有无限多的钱一样。软件膨胀的速度,跟人的欲望是一样的,永远得不到满足。
2019年11月21日
其他

我是如何进行架构设计的

以上是我的一些个人心得体会,并不一定正确。水无常形,兵无常势。大家要根据自己的实际情况来进行合理的架构设计。
2019年10月29日
其他

Java里面的String对象到底神奇在什么地方

是不可变对象。字符串常量池给我们带来这么多困惑,一定也有它存在的意义,它带来的好处就是节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。节省运行时间:比较字符串时,==
2019年10月13日
其他

说说HTTP与TCP的关系

连接在同一时刻只能处理一个请求,意思是说:两个请求的生命周期不能重叠,任意两个HTTP请求从开始到结束的时间在同一个
2019年9月9日