RSS
热门关键字:  java  Ajax  JSP  JSF  Struts
当前位置 : 首页>Java>列表

结合安全发布与有效的不可变性来提升性能

来源: 作者: 时间:2007-10-30 点击:
使多个线程能够共享对可变集合的访问 —— 一种典型方法就是同步对集合的访问 —— 这样做可能会成为性能瓶颈。通过本文学习一种可用于 Java™ 5.0 的技术,然后使用该技术最大限度地减少数据结构(频繁读取,但较少更新)中的性能瓶颈。
使用多个 Java 线程之间共享数据的缺点在于数据访问必须同步,从而避免出现不一致的内容视图,后者可能会导致应用程序失败。例如,Hashtable 类的 put() 和 get() 方法是同步的。因为需要实现同步,所以 put() 和 get() 方法在执行时将同时单独地访问数据;否则,应用程序数据结构可能会被破坏。

当某个应用程序的线程频繁访问这些方法,导致线程出现阻塞时,这些方法的同步点将成为瓶颈。每次只能有一个线程获得内容的访问权。而其他线程必须等待。如果线程出现排队等候(如果不是这样,线程能够进行其他有用操作),性能和吞吐量将下降。当性能分析显示同步方法实际上会导致排队点时,对代码进行优化是有益的。

对于很少进行修改的数据,一种被称为分代数据结构(generational data structure)的技术允许您使用较低的 volatile 开销来安全地发布可变数据结构。当数据结构被频繁访问但很少进行修改时,这将获得性能增益。例如,可以使用未同步的数据结构如 HashMap,而不是同步的数据结构如 Hashtable。该技术的关键内容包括:

发生更新时,制作数据结构的新副本。
完全填充它。
使用 volatile 引用将更新安全地发布到所有客户。
使用该技术,get 和 put 操作永远不会在数据结构的同一个示例上同时执行。将确保两个线程不会尝试同时更新数据结构,并且读取线程会始终查看一致的、最新版本的数据。(即使数据被频繁更新,仍可以使用该方法,不过通过改善并发性而获得的性能增益将损失。频繁地重新填充数据结构可能会抵消由消除同步存取器方法而获得的性能增益。)

成对类的适用性

Hashtable 是一种 Java 类,它提供了多线程共享数据的访问。HashMap 在功能上类似于 Hashtable,但它不是线程安全的。本文所提供的技术适用于其他成对类,它们彼此相似,不同之处在于其中一个类有同步的访问方法,而另一个没有。例如,Vector 有同步的访问方法,而 ArrayList 没有。这两个类都提供相似的功能,而且可以使用本文所讨论的方法。


该技术使用了 Java 语言的三个特性;

自动垃圾收集。当对象的最后一个引用不再使用时,Java 运行时可以自动释放该对象。应用程序不需要进行其他操作,只需确认当应用程序不再使用某个对象时,没有任何引用指向该对象。早期创建的对象会在最后一个客户使用完成后被自动释放。


对象引用的原子性。一个获取对象引用的简单赋值语句是不能被中断的。这意味着只要消费线程可以使用较旧的(但完整的)对象副本生成正确结果,就没有必要围绕单个对象的赋值语句实现同步。但是,必须注意的是仍需在生产者(producer)线程上采取操作,以确保在执行赋值之前创建完成新的对象。正如本文 讨论 部分所述,生产者线程中需要使用同步代码以确保在对象赋值前完成对象创建。但是,不必在消费者线程中加入同步代码,这将消除开销较大的排队点。


Java 内存模型。Java 内存模型规定了 synchronized 和 volatile 的语义。这些规则定义了共享对象及其内容在何时对于除当前正在执行的线程之外的线程是可见的。
为维持两个独立的数据结构实例而对数据结构中的数据进行修改时,您可以使用 Java 语言的以上特性。一旦其中一个被填充,它就不会再次更改。它是有效不可变的。如果允许 get 和 put 操作在同一个数据结构上同时执行,这是比较危险的。本文所讨论的技术将确保所有 put 操作会在执行任何 get 操作之前完成。

技术

清单 1 中的示例代码阐述了该技术:


清单 1. 避免出现排队点的生产者/消费者代码

static volatile Map currentMap = new HashMap(); // this must be volatile to ensure
// consumers will see updated values
static Object lockbox = new Object();

public static void buildNewMap() { // This is called by the producer
// when the data needs to be updated.

synchronized (lockbox) { // This must be synchronized because
// of the Java memory model.

Map newMap = new HashMap(currentMap); // for cases where new data is based on
// the existing values, you can use the
// currentMap as a starting point.

// add or remove any new or changed items to the newMap
newMap.put(....);
newMap.put(....);

currentMap = newMap;

}
/* After the above synchronization block, everything that is in the HashMap is
visible outside this thread. The updated set of values is available to

共2页: 上一页 1 [2] 下一页
最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册