Java基于锁并发和基于无锁并发的比较



Java基于锁并发和基于无锁并发的比较图示。试图解决在一个系统中并发读共享状态时发生的争用问题。StampedLock设计用来优化读性能,它的性能要优于ReentrantReadWriteLock。

第一,在我看完Java现在对锁的实现,我想这是一个关于时间的问题。第二,虽然StampedLock是JDK中的一个非常好的实现,但是这是没有考虑无锁并发算法的情况下。

测试用例

为了比较各自的实现,我需要设计一套API作为测试用例,这个API和用例是相对公平的。例如,这套API必须足够纯洁,不存在消耗时间和性能的累赘,方法是原子性的。一个简单的测试用例是设计一个Spaceship(宇宙飞船)在一个二维空间中移动,它的位置是原子读的。至少有两个属性被读或者被写,并发是事务性的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Interface to a concurrent representation of a ship that can move around
 * a 2 dimensional space with updates and reads performed concurrently.
 */
 public interface Spaceship
 {
 /**
 * Read the position of the spaceship into the array of coordinates provided.
 *
 * @param coordinates into which the x and y coordinates should be read.
 * @return the number of attempts made to read the current state.
 */
 int readPosition(final int[] coordinates);
/**
 * Move the position of the spaceship by a delta to the x and y coordinates.
 *
 * @param xDelta delta by which the spaceship should be moved in the x-axis.
 * @param yDelta delta by which the spaceship should be moved in the y-axis.
 * @return the number of attempts made to write the new coordinates.
 */
 int move(final int xDelta, final int yDelta);
 }

上面的API可以整理提取出一个Position的对象,但是我想消除垃圾产生,还有更直接的使用属性。这套API很容易可以扩展为3维空间并且要求实现的原子性。

现在,我实现不同的Spaceship,这些实现可以运行在一套测试套件中。所有的实现的源代码可以通过这个链接得到。测试套件将运行每一个实现。

每个实现都会测试用不同的线程在4种场景下面进行测试并比较结果,下面是测试用例的说明:

1、1个读线程 – 1个写线程。

2、2个读线程 – 1个写线程。

3、3个读线程 – 1个写线程。

4、2个读线程 – 2个写线程。

所有的测试用例都跑在64位 Java 1.7.0_25, Linux 3.6.30,i7-3632QM 4核2.2G的环境上。如下的吞吐量测量结果是通过5次运行的每秒平均值。为了接近与Java特有的部属环境,这里保证没有线程关联,保证内核隔离。

注意:不同类型的CPU和操作系统可能产生不一样的结果。

结果比较


生成场面图表的数据可以这里下载。

分析

让我感到惊讶的是ReentrantReadWriteLock的性能。我无法想象使用这个实现的在所有用例中的读如此接近,写性能如此低。我的主要想法如下:

1、StampedLock是有很大的提升,除了在读线程增加的情况下,它的性能超过了其他锁实现。

2、StampedLock拥有复杂的API,很容易的锁条件的时候调用了错误的方法。

3、Synchronised在两个线程竞争的情况下,是一个很好的通用实现。

4、线程数量递增的情况下,ReentrantLock是一个很好的通用实现。

5、选择使用ReentrantReadWriteLock的时候,要仔细观察它的适用场景。在使用度量和数据下面做出决定。

6、无锁实现的吞吐量要明显由于基于锁的实现。

结论

很高兴无锁算法的表现要优于锁算法,使用无锁算法是提供高读写性能优化的一种很好的策略。

在我写这个文章还有这个文章的相关代码的经验,我发现无锁算法不只可以提高平均吞吐量,也可以降低延迟并且保证延迟时间的分布均匀。