3、协同中间件并发性能提升实践
协同对其中间件产品升级过程中大量应用了JSR-166所提供的类库和并发功能,并基于其开发了更高层次的类库,获得了深刻的应用经验,下面将就此进行讨论,以期抛砖引玉。
3.1使用原子变量
通常在中间件产品的实现中,有很多情况需要实现多线程使用的计数器或随机数生成器作为ID,这时一般会使用锁来保护共享变量。这样会使得多线程对锁竞争,造成大量线程处于等待状态,浪费了cpu资源,损害了吞吐量。
volatile 变量虽然可以使用比同步更低的成本存储共享变量,但它只可以保证其他线程能够立即看到对 volatile 变量的写入,无法保证读-修改-写的原子性。因此,volatile 变量无法用来实现正确的计数器和随机数生成器。
在这种情况下,可以利用JSR-166包中的原子变量,实现更高效的计数器和随机数生成器。这些原子变量包括 AtomicInteger、AtomicLong、AtomicBoolean 以及数组 AtomicIntergerArray、AtomicLongArray等类型。
下面是计数器的传统实现:
public final class Counter {
private long value = 0;
public synchronized long getValue() {
return value ++;
}
}
下面则是采用原子变量实现的计数器:
public final class Counter {
private AtomicLong value = 0;
public long getValue() {
return value. getAndIncrement();
}
}
3.2使用并发数据结构
Collection 框架曾为 Java 程序员带来了很多方便,但在多核时代,Collection 框架变得有些不大适应。多线程之间的共享数据总是存放在数据结构之中,如 Map、Stack、Queue、List、Set 等。 Collection 框架中的这些数据结构在默认情况下并不是多线程安全的,也就是说这些数据结构并不能安全地被多个线程同时访问。 JDK 通过提供 SynchronizedCollection 为这些类提供一层线程安全的接口,它是用 synchronized 关键字实现的,相当于为整个数据结构加上一把全局锁保证线程安全。
JSR-166中提供了更加高效 collection,如 ConcurrentHashMap/Set, ConcurrentLinkedQueue, ConcurrentSkipListMap/Set, CopyOnWriteArrayList/Set .这些数据结构是为多线程并发访问而设计的,使用了细粒度的锁和新的 Lock-free 算法。除了在多线程条件下具有更高的性能,还提供了如 put-if-absent 这样适合并发应用的原子函数。
使用并发数据结构的优势:
使用者不需要锁
并发性能优异,特别适合多核多线程应用
在并发情况下,更容易使用,不容易出错