技术,互联网,eLearning …

juc同步框架

本文是个人对Doug Lea的The java.util.concurrent Synchronizer Framework一文的简单理解。

J2SE 1.5引入了java.util.concurrent(juc)包,其中包含了一个小的基于AbstractQueuedSynchronizer的同步框架,大部分juc内的同步器(Synchronizers)都基于该框架实现。

同步器需要包含两种类型的方法:

  • 至少一个acquire方法,用于在同步状态允许线程运行之前阻塞组程;
  • 至少一个release方法,用于更改同步状态使得其它线程从阻塞状态恢复;

java.util.concurrent包含多种类型的同步器,每种同步器都支持以下特性:

  • 非阻塞同步和阻塞同步
  • 同步等待超时
  • 可取消(通过中断机制)

同步器可能仅管理独占(exclusive,同一时刻一个阻塞点只有一个线程可以通过)状态,或者也可能管理共享(share,一个阻塞点可能有多个线程通过)状态。

同步器的基本思想很简单,acquire操作:

while (synchronization state does not allow acquire) {
  enqueue current thread if not already queued;
  possibly block current thread;
}
dequeue current thread if it was queued;

release操作:

update synchronization state;
if (state may permit a blocked thread to acquire)
    unblock one or more queued threads;

支持上述操作需要以下三个组件:

  • 原子化管理的同步状态(synchronization state)
  • 线程阻塞和取消阻塞(Blocking and unblocking threads)
  • 维护队列(Maintaining queues)

同步状态

AbstractQueuedSynchronizer使用一个32位的整形变量state维护同步状态,并且暴露出getStae, setState, compareAndSetState方法来存取和更新状态。其中读和写操作是通过volatile语义,compareAndSetState则是通过硬件的compare-and-swap 或 loadlinked/store-conditional指令来保持操作的原子性。

阻塞

在JSR166出现之前,如果不采用内置的monitor机制,没有合适的用于同步器的阻塞和取消阻塞线程的API。具备此功能的两个方法Thread.suspend和Thread.resume由于其自身存在的不可解决的竞争问题而被标记为Deprecated了。java.util.concurrent.locks包引入了LockSupport类,通过LockSupport.park阻塞线程,LockSupport.unpark取消阻塞线程,这两者都是JVM通过本地机制实现的。

维护队列

维护队列是先进先出的非阻塞队列,两个主要的候选者是MCS locks和CLH locks,juc采用的是CLH locks,CLH通常会做为自旋锁(spinlock),不过也可被同步框架所采用,因为其可以很容易的进行取消和超时操作。
CLH队列如下图所示:

入队是一个原子操作

do { pred = tail;} while(!tail.compareAndSet(pred, node));

每一个节点的释放状态保存在其前驱节点内,”自旋锁“的”自旋“表现为如下式

while (pred.status != RELEASED) ; // spin

经过“自旋”后,出队操作仅仅是将头节点指向刚刚获得锁的节点

head = node;

CLH locks的好处是出队和入队都很快,无锁,并且无阻碍。检测是否有线程在等待也很快。

AbstractQueuedSynchronizer队列原始的CLH locks做了改造:

  • 在节点内增加后继域
  • 每个Node里使用状态域控制阻塞,而非自旋
  • 节点的回收依赖GC

还有一些细节的变化,比如头节点的延迟初始化等,看AbstractQueuedSynchronizer的源码可以比较清楚的看到这些。

不考虑以上细节,acquire操作的通用形式如下:

if (!tryAcquire(arg)) {
  node = create and enqueue new node;
  pred = node's effective predecessor;
  while (pred is not head node || !tryAcquire(arg)) {
    if (pred's signal bit is set)
      park();
    else
      compareAndSet pred's signal bit to true;
      pred = node's effective predecessor;
  }
  head = node;
}

release操作的形式如下:

if (tryRelease(arg) && head node's signal bit is set) {
  compareAndSet head's signal bit to false;
  unpark head's successor, if one exists
}

AbstractQueuedSynchronizer框架的使用

一个Mutex的例子,使用状态0代表锁可用,状态1代表锁被占用:

class Mutex {
  class Sync extends AbstractQueuedSynchronizer {
    public boolean tryAcquire(int ignore) {
    return compareAndSetState(0, 1);
  }
  public boolean tryRelease(int ignore) {
    setState(0);
    return true;
  }
}
private final Sync sync = new Sync();
public void lock() { sync.acquire(0); }
public void unlock() { sync.release(0); }
}

试用Spring 3.0对于JSR 303(bean validation)的支持

JSR 303是标准的bean validation框架, 还没有发布正式版,最新的版本是1.0CR5。通过JSR 303,我们可以为域模型里添加@NotNull, @Email等注解,然后在应用程序的不同层里使用这些注解对于域模型做验证。

JSR 303目前有两个实现,分别是agimatec-validationHibernate Validator 4.0,这二者目前都没有正式版,但是测试版已经可以使用了。

Spring 3.0刚刚发布了RC1版,里面提供了对于JSR303的初步支持,试了一下,感觉还不错。

测试程序使用maven, 用的是hibernate的实现。首先,需要在pom里加入相关依赖:

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.0.CR5</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>4.0.0.CR1</version>
    </dependency>

在Spring的配置文件里加入validator factory:

<bean id="validator" >
  <property name="providerClass" 
            value="org.hibernate.validator.HibernateValidator"/>
</bean>

测试用的model:

public class User{
    private String username;

    @NotNull(message = "用户名不能为空!")
    @Size(min = 3, max = 16, message = "用户名最少 {min}, 最大 {max} 个字符")
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

测试用例:

@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
@TransactionConfiguration(defaultRollback = true)
public class TestValidator{

    @Autowired
    @Qualifier("validator")
    private Validator validator;

    @Test
    public void testValidator(){
        User user = new User();
        user.setUsername("a");

        Set<ConstraintViolation<User>> vals = validator.getInvalidValues(user);

        Assert.assertEquals(1, vals.size());
    }
}

运行 mvn test,测试通过。

update: 如果要配合Spring MVC合用,则需要在applicationContext里配置:

  <bean>
    <property name="webBindingInitializer">
        <!-- Configures Spring MVC DataBinder instances -->
        <bean>
            <property name="validator" ref="validator" />
        </bean>
    </property>
</bean>

这样,对于标注为@Controller类里的@Valid标记的对象,Spring会自动做验证。

返回顶部