CountDownLatch 协调线程

JDK8 都快要出来了,在 JDK 5 中仍有许多好宝贝值得去挖掘。提到 JDK5 我们或许只知道它给了我们泛型,其实还有那个并发包 java.util.concurrent 却不那么引人注目,其实就是 NIO。

若是并发包是在某个 JDK 版本中单独奉上,反响就不同了,想想 JDK 6 似乎未带来多少改变--至少对于编程者来说没有明显感受。java.util.concurrent 包中的东西对于我们处理线程带来了很大的便利,例如线程池,线程同步,Future, Callable 等。

这里我记录一下 CountDownLatch 的使用,在此之前在处理

线程 A 等待线程 B,C,D 全部执行完后才继续执行 (比如要每个线程都访问一个 Web 服务,等所有的请求响应成功后进行结果处理)

这样场景的时候,我一般能想到的办法是,初始一个计数器,线程 B,C,D 各自初始化的时候,计数器加一,然后 A 线程等待,每个线程执行完后计数器减一,当计数器为 0 时表明所有任务执行完毕,就通知 A 可以开始运作起来。但这样的方案还是得小心的处理好同步的问题。 阅读全文 >>

JDK8 的 Lambda 表达式 -- 方法引用

Lambda 允许我们定义匿名方法(即那个 Lambda 表达式,或叫闭包),作为一个功能性接口的实例。如果你不想把一个 Lambda 表达式写得过大,那么你可以把表达式的内容分离出来写在一个方法中,然后在放置 Lambda 表达式的位置上填上对那个方法的引用。

方法引用也应看作是一个 Lambda 表达式,所以它也需要一个明确的目标类型充当功能性接口的实例。简单说就是被引用的方法要与功能接口的 SAM(Single Abstract Method) 参数、返回类型相匹配。方法引用的引入避免了 Lambda 写复杂了可读性的问题,也使得逻辑更清晰。

为了应对方法引用这一概念, JDK8 又重新借用了 C++ 的那个 “::” 域操作符,全称为作用域解析操作符。

上面的表述也许不好明白,我看官方的那份 State of the Lambda 也觉得不怎么容易理解,特别是它举了那个例子很难让人望文生意。我用个自己写的例子来说明一下吧。

目前的 Eclipse-JDK8 版还不能支持方法引用的特性,幸好就是在昨天正式版的 NetBeans IDE 7.4 对 JDK8 有了较好的支持,所以在 NetBeans 7.4 中写测试代码。 阅读全文 >>

JDK8 的 Lambda 表达式 -- 词法范围和变量捕获

Lambda 表达式的词法范围,一言以蔽之就是没有引入新的词法范围。这里的词法范围要研究的课题是 this 的指向有没有在变。我们知道在匿名类内部 this 指向的匿名类的实例,这种关系是在编译期就确定的。而在 Lambda 表达式中的 this 与外部的 this 没有差别,也就是说你可以把 Lambda 表达式当成一般的语句来看待,多简单啊,不像 JavaScript 中的 this 被搞的那么魔幻。

可以这么测试:

输出的是同一个地址里的东西:

cc.unmi.testjdk8.TestLambda@28a418fc
cc.unmi.testjdk8.TestLambda@28a418fc

接下来瞧瞧 Lambda 表达式对外层变量的捕获。Lambda 表达式的有个好处就是它是轻量级,可重用单元,并且可捕获外层变量。 阅读全文 >>

JDK8 的 Lambda 表达式 -- 类型推断

现在我们来看看 JDK8 是怎么对 Lambda 表达式进行类型推断的,Lambda 的实际类型叫做它的目标类型(target type),因为 JDK8 沿承了已有的类型系统,所以象这样的写法:

button.addActionListener((ActionEvent e) -> foo());

addActionListener() 方法接收的是一个 ActionListener 类型的参数,所以这里的 Lambda 表达式 (ActionEvent e) -> foo() 代表的就是一个 ActionListener 实例,编译器是怎么知道这一点的,而且相同的 Lambda 是可以表示不同的类型的,见:

Callable<String> c = () -> "done";     //这个 () -> "done" 是 Callable<String> 类型
PrivilegedAction<String> a = () -> "done";  //同样的 () -> "done" 却是 PrivilegedAction<String> 类型

答曰,根据 Lambda 表达式所处的上下文去感知。上下文决定了 Lambda 所期盼的类型,比如说变量声明类型,方法要求的输入参数类型,所以它必须是明确,不能模棱两可。所以一个 Lambda 表达式能否放在某处需要满足以下几个条件:

  1. 期盼的类型必须是一个功能性接口,这样就能唯一定位到那个抽象方法上去,确定方法签名,接下来就是 阅读全文 >>

JDK8 的 Lambda 表达式 -- 实现原理初探

JDK8 使用一行 Lambda 表达式可以代替先前用匿名类五六行代码所做的事情,那么它是怎么实现的呢?从所周知,匿名类会在编译的时候生成与宿主类带上 $1, $2 的类文件,如写在 TestLambda 中的匿名类产生成类文件是 TestLambda$1.class, TestLambda$2.class 等。

我试验了一下,如果使用的是 Lambda 表达式并不会生成额外的类文件,那么字节码里是什么样子的?来看下用  javap -c 反编译出下面文件产生的 TestLambda.class,两个方法,一个是  byAnonymousClass() 使用匿名类,另一个是 byLambda 使用 Lambda 的方式:

阅读全文 >>

JDK 8 的 Lambda 表达式 -- 写法

过去,我们使用匿名类的实现像  ActionListener 这样的接口,即使最简单的情景都需要写上五六行代码,这就显得有些累赘了。因为其实我们在 new ActionListener {} 时就是在创建一个 ActionListener 子类型,重载了方法,并初始化一个实例出来。现在我们手上有了 Lambda 表达式这一武器的话,就变得简单明了了,所以说  Lambda 是一种轻量级的实现机制。

这里是几个 Lambda 表达式的样子:

Lambda 的格式就是三部分:参数列表,-> 分隔符,执行体。其他语方的 Lambda 格式都这样的,可能就是分隔符不一样,如 Ruby 的 |,Scala 的 => 等。

执行体里的 return 只是标明返回到匿名方法的调用者,不是返回值的意思; 最上层是不能用 break 和 continue 关键字的,可用在循环中; 如果 Lambda 表达式有返回值,那么在每一条路径上都要有返回值或是抛出异常。路径上最后一条语句的返回值即为 Lambda 表达的返回值。 阅读全文 >>

JDK 8 的 Lambda 表达式 -- 应用背景

即将到来的 JDK8 最为引人入胜之处非 Lambda 表达式莫数了,这在其他动态语方,如 Ruby, Groovy, Scala 等语言中早已大行其道。一旦 JDK 搭上了 Lambda 这趟车,从此操作事件,线程,处理集合时又大为方便了。关于现阶段如何体验 JDK8 的特性可以参考 抢鲜设置 JDK8 的编程环境,印象

本文主要参考于官方的 State of the Lambda,并对源码或反编译出字节码,作一定的深入以助于各位理解,在 JVM 中是如何实现 Lambda 的。

Lambda 表达式,又称闭包(Closure),或称匿名方法(anonymous method)。这在其他语言中,如 Ruby, Groovy, Scala, JavaScript 等,甚至是在 C# 中运用得如火纯清的特性,JDK8 这才问候他,真有些晚了。没有 Lambda 时,Java 不得不求助于匿名类的回调方法来达到相似的目的,为了捕获外部变量,变量必须声明为 final。

一见到 Lambda,第一个反应就是 Lambda 表达式(局部而已),其实我们这里要说的 JDK8 的 Lambda 包含以下几块内容:

  1. Lambda 表达式,俗称闭包或匿名方法
  2. 方法和构造器引用
  3. 扩充的目标类型和更强的类型推断
  4. 接口中的默认静态方法

Lambda 这一特性使得 Java 也开始向函数式编程倾斜,关键是能更有效的应对并发环境。

说到 Lambda,我们不得不了解一下当前背景,例如下面一个典型的事件处理场景: 阅读全文 >>

JDK 7 代码中对捕获再抛出异常时的处理--即异常类型推断

JDK 发展过程中,第一次变化最大数 JDK1.5,加入了变长参数,泛型。泛型的最大的受益者是集合。JDK7 虽说引进了同时捕获多个异常(Multi-Catch),更聪明一点类型推断,资源的释放等,但我觉得变化还不大。接下来众人期望的 JDK8 的 Lambda 表达式才是激动人心的,恐怕这一特性的大赢家仍是集合。

这样 JDK 才不至于离 C# 太远,纯粹语言上讲,我更景仰 C#,比如它的匿名类型,更不论人家的 Lambda 了。

var person = new { Name = "Unmi", Website = "http://unmi.cc" };
Console.WriteLine(person.Name);

这要到 Scala 中才能见到这种影子。

注: JDK 从 1.5 起就加入了象 Chrome, Firefox 那样的版本党了,所以这个版本也叫做 JDK5,不管是后来的 JDK6, JDK7 等等,其实在命令行下 java -version 显示出来的也还是 1.5.0, 1.7.0_40 这种理智的版本号的。

回过神来,我们要说的是 JDK7 对异常的处理,不讲同时捕获多个异常和 try-with-resource 的处理,而要说的是捕获异常再次抛出时进步,这在某方面得益于 JDK7 类型推断。JDK7 类型推断对于泛型来说,它可以不用这么写 阅读全文 >>