这是why的第 85 篇原创文章

写中间件经常要做两件事:

  • 1.延迟加载,在内存缓存已加载项。
  • 2.统计调用次数,拦截并发量。

就这么个小功能,团队里的人十有八九写错。


上面这句话不是我说的,是梁飞在他的博客里面说的。

梁飞是谁?

据网上的公开资料,梁飞,花名虚极。

2009 年加入阿里巴巴,负责中间件的开发,Dubbo 开源分布式服务框架作者,HTTL 开源模板引擎作者。

2012 年加入天猫,负责手机天猫 APP 的技术团队,见证了天猫双 11 无线化全过程。

热衷参与开源社区建设,传播服务化、SOA、框架设计、移动应用等架构设计理念。

下面这段是我在他的个人博客里面找到的:

https://www.iteye.com/blog/javatar-287026

上周五去杭州面试,早上飞过去,晚上飞回来,有点匆忙,飞机晚点,到那边的时候都快下午一点了,面试了一个小时左右,主要是笔试没做了,省了不少时间。

面试完后,跟着几个熟人转了一圈,学习交流气氛都很好。

周二收到了Offer,看来HR效率很高,赞一个,定的是1月8号入职。

这两天忙于提交辞职申请,就要离开奋斗了近三年的公司,还是有点不舍,公司给了我很多发展机会,领导们对我也特别好,真的很感谢他们。

只是现在新的公司发展机遇更好,我不想放弃,希望在新的岗位上能做得更好,呵呵,流水帐记到这里。

这篇博客,发布于 2008 年 12 月 24 日。

他说的上周五,也就是 2008 年 12 月 19 日。一天之内,从深圳飞到杭州,面试一小时,又从杭州飞回深圳,4 天之后,收到了阿里的 offer。

彼时,梁飞也仅从湖南科技职院软件学院的大门走出来不到 3 年的时间。

文章里面说的“1 月 8 号入职”,也就是 2009 年的 1 月 8 日。

从此,在阿里开启了长达 12 年的升级打怪之路,从 P5 一路杀到了 P9。

众所周知,阿里能到 P9 ,已经不是能力的事儿了,这属于祖上积德。

而梁飞在 2016 年的时候就已经是 P9 了。

上面说的这么多标签,而他最为人熟知的标签应该是:Dubbo 创始人。

在梁飞加入阿里的 1015 天之后,也就是 2011 年 10 月 20 日 23 点 06 分,Dubbo 在开源社区发出了第一声啼鸣:

9 个模块,共计 669 个文件,就是日后这个一路坎坎坷坷、几近夭折、友商续命,最终成为 Apache 顶级开源项目的雏形。

2011 年 10 月 20 日那天晚上,对于梁飞来说注定是一个回忆起来,难以忘怀的夜晚。

他一直进行了13 次提交:

终于在第二天的早上 5 点 25 分给 Dubbo 打上了第一个 tag:2.0.7。

期间还抽空通知官媒发了个微博:

而他自己,第二天的中午,也在自己的博客上公布了这件事情:

为什么 Dubbo 会选在这一天进行开源呢?

我想应该是为了赶上两天之后的 Qcon 全球软件开发者大会:

那一天,才是 Dubbo 真正意义上,站在大众视野里,接受赞扬与嘲讽的开始。

我千辛万苦,找到了近 10 年前,那次大会的分享 PPT,开篇第一页:

十年过去了,白云苍狗,Dubbo 从阿里走了出来,已经真正的飞入寻常百姓家了。

十年间,PPT 上的数据,从总量上来看,就像双十一的销售额一样,不知道翻了多少个翻。

但是转念一想,这是 10 年前的阿里,每天 10 亿次的调用,这是多少技术人一生也接触不到的流量啊。

同时,我在 PPT 里面也发现很多熟悉的图片,比如这张:

左边是现在的 Dubbo 官网,右边是 10 年前 Qcon 大会上的 PPT。

10 年过去了,当这两张图片同框的时候,不知道右边的“老图”,有没有一种老父亲般的欣慰。

如果说上面这个图你的感觉还不是特别的强烈,那么再看看这个:

左边是官网,右边是 PPT。

10 年过去了,整体设计没有发生一丝变化。

我不知道你看到这里的时候想到的是什么,于我而言,真牛逼啊。

一个 10 年前的整体设计,竟然在日新月异、风起云涌的互联网行业一丝不变的屹立了下来。

这是一群工程师智慧的结晶。

Dubbo 第一次面世的 PPT 有 55 页,里面有很多设计从十年后的今天看去也不过时。

我就不一一展示了,有兴趣的朋友,在文末可以看到获取方式。

最后,献上一张最早的 Dubbo 团队的合照:

Java并发编程常识

还记得本文开篇的那句话吗?

https://www.iteye.com/blog/javatar-1963774

这里的 PPT 链接失效了,我历尽千辛万苦,搜索找到了一份。

一共 18 页,一一展示一下。

有的地方是纯知识点,有的地方是代码。

反正我觉得我看明白了,有必要讲一下的地方。我就在图片下面进行一个简短的描述。

走起。

稍微解释一下这一页上面的东西。

这几个命令,我在之前的文章中也用到过:

一个困扰我122天的技术问题,我好像知道答案了。

就是下面这部分:

所以,我知道这个地方是有个坑的。

如果你把命令直接粘过去,会抛出这个错误:

Java HotSpot(TM) 64-Bit Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output

是因为你缺少了一个叫做 hsdis-amd64.dll 的文件(windows平台)。

你需要把这个文件放在 jre/bin/server 的目录下:

然后把命令换成这个:

-XX:+UnlockDiagnosticVMOptions
-XX:+PrintAssembly
-XX:CompileCommand=print,*AtomicInteger.incrementAndGet

再次执行程序,就有汇编输出了:

再主要说一下 CompileCommand 这个参数。

这个参数用来定制编译需求,比如可以指定某个方法不做 JIT 编译,也可以只编译指定的方法等等。

用法是这样的:

-XX:CompileCommand=command,method[,option]

其中的 command 有下面这些选项:

  • exclude,跳过编译指定的方法
  • compileonly,只编译指定的方法
  • inline/dontinline,设置是否内联指定方法
  • print,打印生成的汇编代码
  • break,JVM以debug模式运行时,在方法编译开始处设置断点
  • quiet,不打印在此命令之后、通过-XX:CompileCommand指定的编译选项
  • log,记录指定方法的编译日志,若未指定,则记录所有方法的编译日志
  • 其他命令,option,help

比如 PPT 中的这个用法 -XX:CompileCommand=print,*AtomicInteger.incrementAndGet

含有就是打印 AtomicInteger.incrementAndGet 方法生成的汇编代码。

缓冲行对齐,这个应该是一个需要掌握的知识点,属于关于程序优化的奇技淫巧。

偶现于面试环节。

对齐的目的是为了解决伪共享(False Sharing)的发生。

不知道伪共享的朋友,建议去了解一下。

PPT 中的例子的源码来源是:

com.google.code.yanf4j.util.LinkedTransferQueue.PaddedAtomicReference

需要注意的是,在 Java8 里面,提供了更加优雅的解决方案:@Contented 注解。

这个也是一个非常经典的、关于单例模式的演变过程。

PPT 上一共四个单例模式的写法,我们一个个的看。

这就是我们耳熟能详的饿汉式单例。通过提前初始化的方式,解决了并发问题,保证了单例性。

这里面最关键的就是 final static 关键字。

作者也用了蓝色做以区分,肯定是有深意的。

来,一起背一遍类的加载过程:

加载、验证、准备、解析、初始化。

比如下面这个例子:

public static int value =123

变量 value,只有 static 修饰,那么该变量在准备阶段被赋上初始值,即 0。

在初始化阶段被赋上 123。

但是当再多一个 final 修饰的时候:

public final static int value =123

value 变量在准备阶段就会被赋上 123。

所以,你想想上面的饿汉式,是不是一个道理?

而这一方面的知识点,在《深入理解Java虚拟机》一书里面,写的还是很清楚的。

但是饿汉式有一个明显的劣势,那就是不管应用程序是否使用,都会被初始化出来。

这样并不优雅。

于是,就到了第二段程序,饱汉式。

在需要使用的时候,才进行初始化操作。

但是怎么保证线程安全呢?

通过在方法上加 synchronized 关键字的方式来保证了线程安全?

但是这个玩意真的安全吗?

这可能是面试官最喜欢问的问题之一吧。

要是你之前不知道,也许打死你也想不到还有拿到一个只初始化了一半的对象的情况。

简单来说就是 new Singleton() 不是原子性的,所以会导致 if 条件满足时,instance 并没有完全初始化。

于是就引出了下面这段程序:

传说中的双重检查加锁。

而这段程序最容易被“忽视”的就是 volatile 关键字了。

如果没有 volatile 关键字,那么这个程序也是废的。

还有,需要注意的是梁飞在这里备注了一个 jdk1.5+

为什么呢?

其实答案就写在《Java并发编程实战》的第 287 页:

在这本书里面,形容双重检查加锁的成语是:声名狼藉。

而且在图片里面可以看到:促使该模式出现的驱动力已经不复存在,并不是一种高效的优化措施。

现在,我觉得双重检查加锁主要还是应用在面试环节。

明明就不是一种高效的优化措施,却还是一个高频考点,为什么呢?

还不是八股文害人啊。

接下来说最后一种,也是作者推崇的方式:

这个骚操作的学名叫做“基于类初始化的单例模式解决方案”。

JVM 在 Class 被加载后,且被线程使用之前,也就是类的初始化阶段,会去获取一个锁。

这个锁可以保证多个线程对同一个类进行初始化时,只有一个线程能初始化成功。

而该方法的原理在《Java并发编程的艺术》一书的 3.8.4 章节解释的非常清楚。

什么?这两本书你都没有?

赶紧去搞一本吧,写的还是挺好的,我以前多次推荐过。

额......

这页我也没看明白。

我觉得肯定是有一个上下文的,只是梁飞没有写在 PPT 里面。

导致我看的云里雾里的。

这里只写到了 CountDownLatch 和 CyclicBarrier 。

其实还有一个很重要的、很常用的、工具类:Semaphore。

如果不知道,可以看看这篇文章:

我靠!Semaphore里面居然有这么一个大坑!

如果你上面三个都不知道。

赶!紧!去!学!

打印出来,贴在电脑旁边,朗读并背诵本页。

最后说一句

文中提到的两个 PPT 和一个文件:

  • 阿里巴巴(B2B)的服务框架探索.ppt
  • Java并发编程常识.ppt
  • hsdis-amd64.dll

关注公众号【why技术】,然后在后台回复关键字【ppt】即可获得。

才疏学浅,难免会有纰漏,如果你发现了错误的地方,可以在后台提出来,我对其加以修改。
感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。

我是 why,一个主要写代码,经常写文章,偶尔拍视频的程序猿。

Java并发编程常识的更多相关文章

  1. Java 并发编程常识 —— by 梁飞

    参考 :梁飞 并发编程常识

  2. Java并发编程原理与实战三十五:并发容器ConcurrentLinkedQueue原理与使用

    一.简介 一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素进行排序.队列的头部 是队列中时间最长的元素.队列的尾部 是队列中时间最短的元素.新的元素插入到队列的尾部,队列 ...

  3. Java并发编程原理与实战三十一:Future&FutureTask 浅析

    一.Futrue模式有什么用?------>正所谓技术来源与生活,这里举个栗子.在家里,我们都有煮菜的经验.(如果没有的话,你们还怎样来泡女朋友呢?你懂得).现在女票要你煮四菜一汤,这汤是鸡汤, ...

  4. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  5. 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport

    在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...

  6. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  7. 【Java并发编程实战】-----“J.U.C”:CLH队列锁

    在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格按照FIFO的队列.他能够确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...

  8. 【Java并发编程实战】-----“J.U.C”:CountDownlatch

    上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...

  9. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

随机推荐

  1. 快速理解Python异步编程的基本原理

    第一个例子 假设你需要用电饭煲煮饭,用洗衣机洗衣服,给朋友打电话让他过来吃饭.其中,电饭煲需要30分钟才能把饭煮好,洗衣机需要40分钟才能把衣服洗好,朋友需要50分钟才能到你家.那么,是不是你需要在这 ...

  2. Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!

    Spring Cloud 2020.0.0 没错,Spring Cloud 2020.0.0 正式发布了: 感谢Java技术栈群友通知,想入群的在公众号Java技术栈后台回复:wx,正在使用 Spri ...

  3. Java“微服务”还能这么玩!

      "微服务"加个引号是因为这不是传统定义的微服务架构,顶多算是"小服务"架构,因为服务实例由集群节点统一加载,非独立部署.下面以图说明一下服务调用流程. 一. ...

  4. 拥抱 C/C++ : Android JNI 的使用

    编译工具 CMake 以及 Android 上 JNI 的使用介绍. 编译工具 CMake 在Android Studio 2.2 之后,工具中增加了 CMake 的支持,于是我们有两种选择来编译 c ...

  5. [leetcode]205. Isomorphic Strings同构字符串

    哈希表可以用ASCII码数组来实现,可以更快 public boolean isIsomorphic(String s, String t) { /* 思路是记录下每个字符出现的位置,当有重复时,检查 ...

  6. Linux 路由 静态路由

    Linux 路由 静态路由 目录 Linux 路由 静态路由 一.临时生效,使用命令route A.添加到主机的路由 B.添加到网络的路由 C.添加默认路由 D.删除路由 E.查看所有路由信息 二.临 ...

  7. 迭代器设计模式,帮你大幅提升Python性能

    大家好,我们的git专题已经更新结束了,所以开始继续给大家写一点设计模式的内容. 今天给大家介绍的设计模式非常简单,叫做iterator,也就是迭代器模式.迭代器是Python语言当中一个非常重要的内 ...

  8. RabbitMQ不讲武德,发个消息也这么多花招

    前言 本篇博客已被收录GitHub:https://zhouwenxing.github.io/ 文中所涉及的源码也已被收录GitHub:https://github.com/zhouwenxing/ ...

  9. 漫画|web的主要安全问题

    在此主要说现在市面上存在的4个比较多的安全问题 一.钓鱼 钓鱼: 比较有诱惑性的标题 仿冒真实网站 骗取用户账号 骗取用户资料 二.篡改页面 有一大部分被黑的网站中会有关键字 (在被黑的网站中,用的最 ...

  10. linux安装ftp步骤

    1,查看是否安装了FTP:rpm -qa |grep vsftpd 2,如果没有安装,可以使用如下命令直接安装 yum -y install vsftpd 默认安装目录:/etc/vsftpd 3,添 ...