John Jiang

a cup of Java, cheers!
https://github.com/johnshajiang/blog

   :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
  131 随笔 :: 1 文章 :: 530 评论 :: 0 Trackbacks
Java Concurrent Animated
    在最新一期的Java Magazine中有一篇访谈,介绍了一个学习Java并发编程的动画应用Java Concurrent Animated。该应用以十分直观的方式展示了Java并发工具包中的每一个重要组件,降低了学习Java并发编程的难度。(2013.12.07最后更新)

Java Magazine:有多少人已经试用过了你的Java Concurrent Animated应用?
Grazi:该应用是在2009年7月被引入的,从那时算起,已经有了大约20000的下载量。但考虑到已有约一千万的Java开发者,这个下载量才只是开始。按国家区分,下载最多的分别是美国(23%),印度(14)和中国(7%)。
    你可以下载一个可以执行的JAR文件,然后仅需双击它就可以运行了。该应用是由菜单驱动的,或者也可以使用向上或向下键在不同的图像和动画之间进行导航。它能运行在诸如Windows,Mac,Linux等等所有的平台上。它要求安装Java SE 6或更高的版本。

Java Magazine:对这个应用最典型的反馈是什么?
Grazi:大家告诉我这个工具很好用。许多人确实对此感到兴奋,尤其是那些正试图向团队教授合适并发技术的老师与领导们。Java是最早在核心类库中引入并发的语言之一。在当时,这是一个很强大的特性,但我们很快就发现一个非常优秀的程序员与会写出很糟糕的并发代码。进行恰当的并发编程是一件困难甚至是不可能的事情,但是如何人们能花些时间去理解一些现有的框架,那么在进行并发编码时所产生潜在错误就会变得极少。
    例如,去看看Java内存模型。开发者经常忽视Java内存模型,而像个幸福的傻瓜一样在编码,那么他们的程序会不太正常,因为Java虚拟机(JVM)和服务器可能无法利用到由Java内存模型所提供的优化。由于内核在速度与数量上都有了增长,厂商们期望能够高效地利用到这些内核,然而由于错误的并发管理,本来如期运行的程序却开始遇到了一些零星的错误。

Java Magazine:你是说,这个应用会以我们所虚构的方式去使开发者们能够更快且直观地掌握Java并发的原理与实践?
Grazi:那是达到这一目的一个有趣的途径。你知道的,Java Concurrent Animated并不是一个Flash动画。它是一组可交互的Java程序,也即,每个动画都是真地在使用它所要演示的并发组件。在屏幕的右边是一个展示代码片断的面板,由于动画的运行,它会动态地高亮显示及恢复正在执行的代码。
    让你给你一个例子,这个例子发生在ReadWriteLock这个动画中。ReadWriteLock用于确保数据的一致性。它允许不受数量限制的线程去获取读锁,并能并发地对这个锁进行操作。但是,写线程在获取这个锁之前只能等待所有的读线程执行结束。一旦一个写线程获得了这个锁,那么其它的读线程或写线程将无法获取它。
    假设一个写线程正在等待正在执行中的读线程去释放这个读锁,但突然一个新的读线程跑过来了。那么谁应该获得这个锁会比较好呢?这个新的读线程应该跑到写线程前面去吗?毕竟,如果其它的读线程已经获得了这个锁,那么新来的读线程为什么要去等一个尚在等待中的写线程呢?而这实际上这正是Java 5所干的事儿。但某次我在Java 6上运行这个动画时,我注意到行为发生了改变。即,随后而来的读线程在获取到这个锁之前可能要等待所有的写线程先释放锁。
    我认为这个新的行为是一个BUG,且向并发专家Heinz Kabutz博士提及了此事。博士解释道,这不是一个错误,而一个特性。如果允许新到的读线程跳到正处于等待中的写线程的前面去,这就存在产生线程饥饿条件的高风险。因为,存在一种很大的可能性,可能没有任何写线程能获得这个锁,它们将永远等待着。这就是一个如何使用动画去警示依赖于JVM运行时版本的线程行为的例子。

Java Magazine:以动画教程的形式来展示特殊值,在Java并发编程中有何与众不同吗?
Grazi:Miller定律教会我们,我们的大脑在某一时刻能处理的思维的数量是有限的。人类大脑倾向于进行顺序的思维处理,那么即便我们能够克服身体上的束缚,并能够去正确地进行理解,在以后也很难返回至前去重新构造前面的思维处理。可以肯定地是,如果另一个开发者在以后能深入对其进行研究,那么仍然非常难以从原有的思维成果中再次捕捉到认知轨迹。这样的话,脆弱的代码就会很突然地不能正常工作了。
    通过使用框架,我们不仅将并发编程委托给了创建和维护该框架的聪明开发者们,而且还为沟通设计时引入了一个词典。所以,我可以说,“下面的代码会当作CyclicBarrier去执行”,而人们会明白那是什么意思。通过为java.util.concurrent中的所有组件都引入一个可交互化的动画应用,开发者们点着鼠标就能很方便地将他们所探究的功能进行可视化,使理解这些算法变得真心简单了。

Java Magazine:你当时正在研究某些直觉,这些直觉可以帮助更方便地学习并发编程。从开发者的反馈来看,这些直觉看起来是有效的。
Grazi:是的。例如,我前面解释的ReadWriteLock基本功能。读者们可能理解了,也可能没有。现在让我们看看这个与其有关的动画,如图表1所示。

    绿色线程是读线程,最上面的白色线程(带着菱形箭头)是一个写线程,它下面的白色线程是一个新的读线程,该线程在获取锁之前必须要等待所有的读线程与写线程执行完毕。如果你点击按钮并观看这些动画,会比通过浏览繁冗的解释性文字去进行理解要简单得多了。

Java Magazine:Heinz Kabutz评论道,Java被构建成能够一次性做许多事情,而这正与并发完全相关。你的学习系统是如何提高程序员的技能,以便他们能降低并发错误的风险。
Grazi:经常地,当我要努力克服一个并发问题时,我知道解决方案就存在于某个设计模式中,但是哪一个呢?在开发者探寻一个正确解决方案时,Java Concurrent Animated为他们提供了一个所有方案的目录;在激发出正确方案的过程中,它扮演着向导的角色。

Java Magazine:当你管理的团队正在使用Java并发,并且你和你的团队都想更好地去理解Java并发,Java Concurrent Animated有着它的出发点。是什么导致你使用动画呢,能描述下这个过程吗?
Grazi:我的培训是针对投资部门的服务器端Java应用,在那里,并发是一个通常都会受到关注的问题。交易员们要求延迟要低,这样可以确保他们在这个需要于一毫秒窗口时间内捕捉交易机会的比赛中不会成为失败者。批量处理也要求快速完成,等等。所以我开始看到那些可怕的只写(write-only)组件,这些组件使人们在并发编程挣扎着。我自己也身处其中。
    某天下午,我正坐在机场内,将要前往芝加哥为我的团队做一个关于并发的讲演。我正对讲演的PPT进行最后的处理,那组幻灯片着重演示了每一个重要的组件。为了引导我浏览java.util.concurrent中每个并发组件的状态,我写了一些状态机,它们展示了一些供我参考用的简单文本消息。在之前的生涯中,我曾在一家互联网创业公司中开发交互式的游戏,所以我懂得许多与动画相关的知识。这使我想到可以将PPT替换成一组交互式的动画应用,那会更为直观。
    在等飞机的过程中,我写了一个初步的动画引擎,然后在我的状态机中调用了这个引擎。到了第二天早晨,我已经有一个可用的原型程序。多年来,我一直致力于这个框架,并且吸引了其他专家的建议。我传递过一份早期版本给Brian Goetz,令人惊讶的是,他为每个动画程序都给出了建议。我将他的所有建议到吸收到了该框架中。在我的第一次JavaOne讲演中,Kirk Pepperdine加入了进来。他建议为动画应用在真正的PPT中加入描述,以便讲演者能记住正在讨论的内容。随后我加上那些描述,这确实非常有用--不只是对讲演者有用,对于终端用户也很有用。Heinz Kabutz也加入了那场讲演,并建议修改某些动画,以使它们更为直观。
    在另一场讲演中,一个很有激情的软件咨询师Oliver Zeigermann指出,很显然缺少了针对ConcurrentHashMap的动画。我问他是否有兴趣贡献这个动画,随后他添加了那个很有价值的动画程序。

Java Magazine:你能带着我们过一遍Java并发工具包中的类吗?并能否解释一下这些动画程序是如何使开发者们更易于深入理解这些类?
Grazi:好的,但在没有动画程序的情况下确实很难办到。让我们看看CyclicBarrier,它有两个重要的状态,如图2和图3所示,它们展示了一个障碍和四个成员。在图2中,我们可以看到有三个成员已经到了,所以它们被阻止继续前进。图3展示了,一旦第四个成员也到达了,每个成员又可以向前走了。



    这就形象地诠释了障碍的概念,亦即,在所有成员到达障碍点之前,每个成员必须等待。随着并发组件复杂度的增加--例如Fork/Join的动画,以及那些演示原生的wait和notify机制的动画--使用动画程序的好处就更不肖说了。

Java Magazine:谈谈在创建这些动画程序的过程中所遇到的一些挑战。
Grazi:有一些挑战。开始时,线程被表示成箭头。对于多数并发组件,这种表示法是有效的。后来我们必须提供一个可视化方案,不仅要表示线程,还要表示BlockingQueue中的对象。所以,我不得不引入一个称之为"精灵类型(sprite-type)"的概念,然后我们有了一个箭头型的精灵类型和一个新的"对象"型的精灵类型。后来,ConcurrentHashMap和AtomicInteger又需要新的精灵类型,因为我们试图要对他们的计算与交换行为进行可视化。
    后面又来了Fork/Join,新的挑战就是如何去表现那些完全不同于现有框架所表现的可视化部件。还有一个挑战,即Fork/Join动画需要解决一个实际的问题,但这个动画应该解决一个什么样的问题呢?
    开始时,我让这个动画程序去求Fibonacci数列,但却行不通。我在这个问题上纠结了两天时间,直到我认识到Fibonacci数列(Fn+1=Fn+Fn-1)无法高效地并行化,因为每个值都依赖于它前面的值。所以,无论你如何试图对其实施并行化,它天生就是一个顺序化的计算。所以我换成了另一个问题--查找数组中的最大元素,这样就好了。在这个动画中,你可以很精确地看到如何使用一个随机的数列去解决这个问题(如图4所示)。


Java Magazine:你都在哪里讲演过这些动画程序?
Grazi:在JavaOne中讲演过几次,在其它的许多会议,如奥斯陆中的JavaZone,苏黎世的Jazoon,纽约的QCon,以及许多SIG(特别兴趣组)和JUG(Java用户组)中也都讲演过。我喜欢讲演,我也喜欢周游世界,而Java Concurrent Animated为我提供了一个极好的机会去做这两件事情。它总能获得极高的评价。
    Java Concurrent Animated的讲演提供了一种意识,并且它们也向出席的开发者们展示了下载这一框架的价值,而且它还展示了,如果你拥有了框架和灵感启迪,学习并发编程会是多么的容易。
posted on 2013-12-07 17:45 John Jiang 阅读(2301) 评论(1)  编辑  收藏 所属分类: JavaSEJavaConcurrency翻译

评论

# re: Java Concurrent Animated(译) 2013-12-14 23:55 左岸
说实话还真没有了解的这么深过 学习了  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: