Java 普通线程池与 ForkJoinPool 的效果对比

Java 多线程编程常用的一个接口是 ExecutorService, 其实就一个线程池的接口,一般由两种方式创建线程池,一为 Executors 的工厂方法,二则创建 ForkJoinPool 实例,当然也有直接使用 ThreadPoolExecutor 的。

关于什么时候用 ForkJoinPool 或普通的线程池(如 Executors.newFixedThreadPool(2) 或 new ThreadPoolExecutor(...)) 不过多的述说。如果要运用到 ForkJoinTask 的话就要用 ForkJoinPool, 它是 Java7 新引入的线程池类型。

关于 Java7 的 fork-join 框架可参考很多年前的一篇 Java 的 fork-join 框架实例备忘。ForkJoinPool 的一个典型特征是能够进行 Work stealing。它也是 Akka actor 效率高效的一个有力保证。

本文只能某一种情形下在选择普通线程池与 ForkJoinPool 的区别,直接说吧,普通线程更容易造成死锁,而 ForkJoinPool 却能应对相同的状况。 阅读全文 >>

运行时动态创建 Spring Bean

通常我们注册 Spring Bean 是通过像 @Named, @Bean, @Component, @Service 这样的注解来注册的,或者用更为古老的 XML 配置文件的方式。难免有时候要根据实际业务需求在 Spring  运行期间动态注册 Spring Bean, 比如基本某种形式的配置文件或系统属性来注册相应的 Bean, 这好像又回到了 XML 文件注册方式,也不尽然。

那为什么在运行期还要去注册 Spring Bean 呢,直接 new 对象不行吗?当然行得通,不过这样的话就不能更好的使用到  Spring IOC 的好处了。像待注册的 Bean 构造函数可以直接用到其他的 Spring  对象,或 @Value 引入环境变量,还有 @PostContruct 这样的行为。

最初思考如何注册 Spring Bean 时还是费了不少周折,如今清晰了许多。了当的说,不管是 Spring  初始时还是运行时,注册 Bean 的关键(应是唯一) 入口就是 BeanDefinitionRegistry 接口的方法 阅读全文 >>

用 Java 把内存中的表格数据合并到 SQL Server 表中

承接近两年前的 用 PreparedStatement 向 SqlServer 中一次性插入多条记录,其文后用 User-Defined Type 可用下面简单的代码把 Java 本地内存中表格数据一股脑的刷入到 SQL Server 数据库表格中

String sql = "INSERT INTO Customers SELECT * FROM ?";
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) conn.prepareStatement(sql);
SQLServerDataTable dataTable = ..... // 生成好的本地表格数据
pstmt.setStructured(1, "CustomersTableType", dataTable);
pstmt.execute();

上面的 dataTable 本地表格类型变量容易生成,关键是必须在正式数据库数须预先用 CREATE TYPE 创建好 CustomersTableType 这个用户自定义类型,这会受权限的约束。如果由 DBA 预先完全依照目标表来创建好这个用户自定义类型,又无法确定是否总是要操作该目标表的所有字段。

数据库是允许我们创建临时的用户自定义类型 阅读全文 >>

等待所有的 CompletableFuture 完成

现实中有这样的用法,创建一批在线程池中运行的  CompletableFuture 实例,然后等待它们全部执行完再继续后面的操作。比如说 AWS 的 Lambda, 单单提交任务到线程池,不等待所有任务全部完成便退出主线程的话,AWS 便认为 Lambda 执行完毕,无视线程池中正在执行的任务而强行结束该 Lambda 实例。

以往我们通常的作法如下

如果所有的任务均无异常,上面的代码能得到预想的结果,只要上面打印出 all done 的话真的就是表明所有的任务都完成了。但是在循环 join futures 中的每一个 CompletableFuture 时,只要碰到任意一个任务有异常时,便立即抛出给外部线程,不在乎是否还有其他任务正在执行。此时,如果外部未予捕获,当然 阅读全文 >>

转换 Iterator 为 Java 8 的 Stream

Java 中有关抽象的可遍历的对象有 Iterator, Iterable 和 Java 8 的 Stream, Iterable 可简单的用如下代码转换为 Stream

StreamSupport.stream(iterable.spliterator(), false)

再回过头来,为什么要把 Iterator 或 Iterable 转换为 Stream, 因为 Iterator 和 Iterable 只提供有限的遍历操作,如 Iterator 接口的全部四个方法

hasNext()
next()
forEachRemaining(consumer)
remove()

同样 Iterable 也只有 iterator(), forEach(consumer), 和 spliterator() 方法。而 Java 8 的 Stream 就大不一样的,带有大量的链式操作方法,如 filter, map, flatMap, collect 等。

因此如果我们已有一个 Iterator 类型,能够被转换为 Stream 类型的话将会大大简化后续的转换,处理操作。具体的从 Iterator 到 Stream 的转换方式有两种 阅读全文 >>

试手 RxJava 2.x 及对线程的初步理解

在进行数据流处理过程中,需要一个高效苗条的流处理组件,比如对输入流能进行分组(窗口),能进行流量控制(Back Pressure - 背压),这也就涉及到响应式编程,流处理框架。这方面如果直接基于 Akka actor 来构建 Akka ActorSystem 也是比较复杂,依赖的组件也不少。还有构筑在 Akka actor 之上的 Akka Streams,再往上的 Flink Streaming,它们都有像滑动,滚动窗口的概念,但是依赖更不得了。一个基本的 Flink Streaming 的项目会依赖到 45 M 以上的第三方组件,如果用它来写一个数据流处理的共享组件,那真是要命。Spring 5 也开始带上了自己的 Reactive-Streams 实现 Spring Reactor, 想要把它从 Spring 中单独抽离出也非易事。

Flink Streaming 组件依赖:org.apache.fling:flink-streaming-java_2.12:1.80, 会依赖于其他诸如 akka-stream, akka-actor, flink-core, flink-clients, scala-library 等非常多的东西

而另一个著名的响应式框架 RxJava 2 就清爽多了,完全没有第三方依赖,要说有也就是定义了四个接口的 reactive-streams(2 KB 大小),就自身那个  rxjava-2.2.9.jar 包只有 2.3 M,这才叫轻量级。因为它设计来是能被应用于 Android 客户端应用的,Andriod 上的 rxandriod-1.2.1.aar 只有 9 K。所以 RxJava 2.x 太适合用来写一些小的共享组件了。 阅读全文 >>

Java 与'嵌入式' PostgreSQL 数据库的单元测试

在我们对数据库 DAO 类进行单元测试时,通常不应该依赖于一个外部数据库,所以会选用特定比较接近于真实数据库类型的内存或嵌入式数据库,如 HSQLDB(HyperSQL), H2, Derby 等。但总难免会用到特定数据库的特性,这时候就无法用前述各种数据库进行测试了。非要单元测试中覆盖到所用的数据库特性的话可以选择用 docker,如 Testcontainers, 经过模块扩展,它可以由 docker 来启动许多种类型的数据库,MySQL, Postgres, Oracle-XE, MS SQL Server, Couchbase 等等,详情见 Database containers。刚了解到的是它的模块化的无限可能,像支持 Kafka Containers 和 Localstack Module 等。

这里就不走 Testcontainers 那条路 -- 要求构建服务器上也要有 docker。早先希望能找到一种嵌入式或内存 PostgreSQL 数据库,后来发现 PostgreSQL 未能提供 In-Process 和 In-Memory 的启动方式,好在 PostgreSQL 是开源,有人可以把它改造为小型的可由测试代码启停的本地数据库。有两个具有代表性的组件,分别是 OpenTable Embedded PostgreSQL ComponentEmbedded PostgreSQL Server,它们都号称是 Embedded,所谓嵌入式,其实是进测试进程外的数据库。

下面简单体验下两个组件的用法 阅读全文 >>

使用 Google Guava Striped 实现基于 Key 的并发锁

写 Java 代码至今,在应对可能冲突的共享资源操作时会尽量用 JDK 1.5 开始引入的并发锁(如 Lock 的各类实现类, ReentrantLock 等) 进行锁定,而不是原来的 synchronized 关键字强硬低性能锁。

这里是应用 JDK 1.5  的 Lock 的基本操作步骤

private Lock lock = new ReentrantLock();
private void operate() {
    // 安全操作 ....
    lock.lock();
    try {
        // 对共享资源的操作 ...
    } finally {
        lock.unlock();
    }
}

如此,operate() 就是一个线程安全的方法,任何对它的调用都安排到了一个队列里等着。但有时候上锁需要考虑更细的粒度,下面是一个演示案例,引出第一个问题

阅读全文 >>

Java 元注解及 Spring 组合注解应用

Java 1.5(Tiger) 个人认为最为激动人心的两个特性是泛型与注解(Java Versions, Features and History)。泛型自然是不必说了,注解对 Java 世界的改变比泛型伟大的多(现在框架的注解配置),在 Java 1.5 之前我们只能在 Javadoc 注释中做文章,于是只能用 XDoclet 那样不伦不类的东西。Java 的注解发展到现在几乎可以使用在书写代码时的任何地方,见 java.lang.annotation.ElementType 中的类型,囊括了 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER(since 1.8), TYPE_USE(since 1.8)。

Java 1.5 基本确定了注解的基本框架,包括元注解(meta-annotation); 直到 Java 8 又扩展了注解的使用范围,列举如下:

创建类实例
new@Interned MyObject();

类型映射
myString = (@NonNull String) str;

implements 语句中
class UnmodifiableList<T> implements@Readonly List<@Readonly T> { ... }

throw exception声明
void monitorTemperature() throws@Critical TemperatureException { ... }

解析前面 ElementType Java 8 增加的 TYPE_PARAMETER和 TYPE_USE 注解使用新场合。ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中。ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中(如: 声明语句、泛型和强制转换语句中的类型) 阅读全文 >>

Spring ServiceLocator 介绍及应用

在 Spring 中应用 ServiceLocator 方式来获取 Spring Bean 的介绍参考了那么多,其实还是数官方的 ServiceLocatorFactoryBean JavaDoc 文档最言简意该了。Spring 的 ServiceLocator 仿佛用处不大,说到底就是类似于下方找寻某个 Spring Bean 一样:

ApplicationContext context = ...;
Service service = context.getBean(ServiceImpl.class);
Service service = context.getBean("myService");

只是有了 ServiceLocatorFactoryBean(它本质上就是一个 FactoryBean) 后我们不需要直接与 ApplicationContext 打交道,且多个的 Spring Bean 可以从相关的一个 FactoryBean 获得。下面用两个例子来演示(代码中刨去了 package 和 import 部分的代码)

一:实现类只有一个 Spring Bean 时

接口类 Parser(我们要定位就是它的实现类) 阅读全文 >>