Java 7 Fork/Join Framework

在 Java7引入的诸多新特性中,Fork/Join 框架无疑是重要的一项。JSR 166旨在标准化一个简单可扩展的框架,将并行(parallel )计算的通用工具类组织成一个类似java.util中Collection一样的包。其目标是使之对开 发人员易用且易维护,同时该框架也旨在为并行计算提供一个高质量实现。目前已经有多个新的类和接口被添加到该框架中了。

该新特性主要是解决Java社区中对于如synchronized,wait和notify等操作的需求。Fork/Join框架设计目标就是可以容易地将算法并行化、分治化。开发人员曾多次想用自己(在非底层实现)的并发机制实现这一目标,因此新框架的想法是提供标准化和效率最高的并发工具协助开发人员实现各种多线程应用。其所需的类和接口都位于java.util.concurrent包中。

本文将描述Fork/Join框架及其如何用于解决Java并行问题(这些问题可参考本专题的第一部分)。

阅读全文

Java集合总览

这篇文章给出了所有标准的Java集合(Collection)的概要。根据它们不同的属性和使用场景进行了分类。还介绍了在不同的集合类型之间转换的方式。

阅读全文

Java 8 Stream探秘

在现代的Java应用程序中很少不用到集合类和数组。 可以对集合进行增,删,改,插, 统计(聚合aggregate)。 这些操作的概念在SQL操作上也会用到。 但是对集合的操作却没有像SQL那样方便简捷。 为什么我们不能实现一种类似SQL语句一样方便的编程方式呢, 去取代一遍又一遍loop遍历的方式处理集合和数组中的数据?
另外,对于大数据量的集合, 能不能充分利用多核的优势, 并行的处理?
Stream是就是这种处理数据的风格, 一种流式风格。 这种风格在其它语言中也有实现, 比如Javascript (Node.js stream)。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

1
2
3
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+

一个简单的例子:

1
2
3
4
5
6
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();

阅读全文

java aio 编程

Java NIO (JSR 51)定义了Java new I/O API,提案2000年提出,2002年正式发布。 JDK 1.4起包含了相应的API实现。
JAVA NIO2 (JSR 203)定义了更多的 New I/O APIs, 提案2003提出,直到2011年才发布, 最终在JDK 7中才实现。
JSR 203除了提供更多的文件系统操作API(包括可插拔的自定义的文件系统), 还提供了对socket和文件的异步 I/O操作。 同时实现了JSR-51提案中的socket channel全部功能,包括对绑定, option配置的支持以及多播multicast的实现。

当前很多的项目还停留在JAVA NIO的实现上, 对JAVA AIO(asynchronous I/O)着墨不多。 本文整理了一些关于JAVA AIO的介绍,以及netty对AIO的支持。
以下内容只针对socket的I/O操作, 不涉及对文件的处理。

阅读全文

Java 8 Lambda 揭秘

再了解了Java 8 Lambda的一些基本概念和应用后, 我们会有这样的一个问题: Lambda表达式被编译成了什么?。 这是一个有趣的问题,涉及到JDK的具体的实现。 本文将介绍OpenJDK对Lambda表达式的转换细节, 读者可以了解Java 8 Lambda表达式背景知识。

阅读全文

Java 8 默认方法和多继承

以前经常谈论的Java对比c++的一个优势是Java中没有多继承的问题。 因为Java中子类只能继承(extends)单个父类, 尽管可以实现(implements)多个接口,但是接口中只有抽象方法,方法体是空的,没有具体的方法实现,不会有方法冲突的问题。

这些都是久远的说法了,自从今年Java 8发布后, 接口中也可以定义方法了(default method)。 之所以打破以前的设计在接口中
增加具体的方法, 是为了既有的成千上万的Java类库的类增加新的功能, 且不必对这些类重新进行设计。 比如, 只需在Collection接口中
增加default Stream<E> stream(), 相应的SetList接口以及它们的子类都包含此的方法, 不必为每个子类都重新copy这个方法。

这是一个折衷的设计,带来的问题就是为Java引入了多继承的问题。 我们知道, 接口可以继承接口, 类可以继承类和实现接口。 一旦继承的类和实现的接口中有
相同签名的方法, 会出现什么样的状况呢? 本文将探讨各种情况的多继承, 以便能清楚的理解Java多继承的规则。

阅读全文

Java 8函数式接口functional interface的秘密

函数式接口(Functional Interface)是Java 8对一类特殊类型的接口的称呼。 这类接口只定义了唯一的抽象方法的接口(除了隐含的Object对象的公共方法), 因此最开始也就做SAM类型的接口(Single Abstract Method)。

为什么会单单从接口中定义出此类接口呢? 原因是在Java Lambda的实现中, 开发组不想再为Lambda表达式单独定义一种特殊的Structural函数类型,称之为箭头类型(arrow type), 依然想采用Java既有的类型系统(class, interface, method等), 原因是增加一个结构化的函数类型会增加函数类型的复杂性,破坏既有的Java类型,并对成千上万的Java类库造成严重的影响。 权衡利弊, 因此最终还是利用SAM 接口作为 Lambda表达式的目标类型。

JDK中已有的一些接口本身就是函数式接口,如Runnable。 JDK 8中又增加了java.util.function包, 提供了常用的函数式接口。

函数式接口代表的一种契约, 一种对某个特定函数类型的契约。 在它出现的地方,实际期望一个符合契约要求的函数。 Lambda表达式不能脱离上下文而存在,它必须要有一个明确的目标类型,而这个目标类型就是某个函数式接口。

阅读全文

Java位操作指南

原码,反码和补码

这个是计算机的基础知识了。

+3的原码为00000011
-3的原码为10000011
第一位是符号位。

+3的反码为00000011
-3的反码为11111100
正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

+3的补码为00000011
-3的补码为11111101
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

使用补码可以将减法变为加法,而且0的问题也会解决。

Java中使用补码的格式保存byte,short,int,long类型的数据。 ( two's complement integer.)

位操作符

  • & 与。 按补码的每一位进行与操作。
  • | 或。 按补码的每一位进行与操作。
  • ^ 异或。 按补码的每一位进行与操作。
  • ~ 取反。 按补码的每一位进行与操作。
  • >> 带符号右移,高位补符号位。 正数右移一位相当于除以2。
  • >>> 无符号右移。高位补0。 正数右移一位相当于除以2。
  • << 左移,低位补0。 正数左移一位相当于乘以2。

除了~是一元操作符外其它都是二元操作符号。

阅读全文

Java Buffer编程基础

Java 1.4中在java.nio包中增加了Buffer类以及一些处理基本数据类型的子类(除了boolean型) ,用来提供为基本数据类型(primitive) 的数据提供一个容器。
何谓Buffer? Buffer 是一个线性的有限长度的特定基本数据的序列。 除了基础数据外,它还包括一些基础操作和属性, 比如capacity, limitposition

实际使用中使用特定的子类来处理数据。每个子类都定义了两套get/put的操作。

  • 相对位置操作 (Relative )。 从当前位置position读写一个或者多个元素, 并position增加相应的数值。 如果一个get请求的数据超过了limit的位置,会抛出BufferUnderflowException异常。 如果一个put操作超过了limit的限制, 会抛出BufferOverflowException异常。不管上面哪种情况,没有数据被传输。
  • 绝对位置操作 (Absolute )。 显式地提供index, 不会影响position的值。 如果索引超过limit会抛出IndexOutOfBoundsException异常。

数据也可以通过Channel的I/O操作如write,read 写入或者读出。
显然, Buffer只有写入了数据才可能有意义的数据读出。

Buffer类并不是线程安全的, 使用时要特别小心, 避免多线程同时读写同一个Buffer。 万不得已, 需要为读写操作加锁。

阅读全文