来自http://www.iteye.com/topic/363625
2、Lock
多线程编程中常常要锁定某个对象,之前会用synchronized来实现,现在又多了另一种选择,那就是java.util.concurrent.locks。通过Lock能够实现更灵活的锁定机制,它还提供了很多synchronized所没有的功能,例如尝试获得锁(tryLock())。
使用Lock时需要自己获得锁并在使用后手动释放,这一点与synchronized有所不同,所以通常Lock的使用方式是这样的:
- Lock l = ...;
- l.lock();
- try {
- // 执行操作
- } finally {
- l.unlock();
- }
java.util.concurrent.locks中提供了几个Lock接口的实现类,比较常用的应该是ReentrantLock。以下范例中使用了ReentrantLock进行节点锁定:
- package service;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- /**
- * 节点类
- *
- * @author DigitalSonic
- */
- public class Node {
- private String name;
- private String wsdl;
- private String result = "PASS" ;
- private String[] dependencies = new String[] {};
- private Lock lock = new ReentrantLock();
- /**
- * 默认构造方法
- */
- public Node() {
- }
- /**
- * 构造节点对象,设置名称及WSDL
- */
- public Node(String name, String wsdl) {
- this .name = name;
- this .wsdl = wsdl;
- }
- /**
- * 返回包含节点名称、WSDL以及验证结果的字符串
- */
- @Override
- public String toString() {
- String toString = "Node: " + name + " WSDL: " + wsdl + " Result: " + result;
- return toString;
- }
- // Getter & Setter
- ...
- }
- package service;
- import java.util.concurrent.Callable;
- import java.util.concurrent.locks.Lock;
- import java.util.logging.Logger;
- import service.mock.MockNodeValidator;
- /**
- * 执行验证的任务类
- *
- * @author DigitalSonic
- */
- public class ValidationTask implements Callable<Node> {
- private static Logger logger = Logger.getLogger( "ValidationTask" );
- private String wsdl;
- /**
- * 构造方法,传入节点的WSDL
- */
- public ValidationTask(String wsdl) {
- this .wsdl = wsdl;
- }
- /**
- * 执行针对某个节点的验证<br/>
- * 如果正有别的线程在执行同一节点的验证则等待其结果,不重复执行验证
- */
- @Override
- public Node call() throws Exception {
- Node node = ValidationService.NODE_MAP.get(wsdl);
- Lock lock = null ;
- logger.info("开始验证节点:" + wsdl);
- if (node != null ) {
- lock = node.getLock();
- if (lock.tryLock()) {
- // 当前没有其他线程验证该节点
- logger.info("当前没有其他线程验证节点" + node.getName() + "[" + wsdl + "]" );
- try {
- Node result = MockNodeValidator.validateNode(wsdl);
- mergeNode(result, node);
- } finally {
- lock.unlock();
- }
- } else {
- // 当前有别的线程正在验证该节点,等待结果
- logger.info("当前有别的线程正在验证节点" + node.getName() + "[" + wsdl + "],等待结果" );
- lock.lock();
- lock.unlock();
- }
- } else {
- // 从未进行过验证,这种情况应该只出现在系统启动初期
- // 这时是在做初始化,不应该有冲突发生
- logger.info("首次验证节点:" + wsdl);
- node = MockNodeValidator.validateNode(wsdl);
- ValidationService.NODE_MAP.put(wsdl, node);
- }
- logger.info("节点" + node.getName() + "[" + wsdl + "]验证结束,验证结果:" + node.getResult());
- return node;
- }
- /**
- * 将src的内容合并进dest节点中,不进行深度拷贝
- */
- private Node mergeNode(Node src, Node dest) {
- dest.setName(src.getName());
- dest.setWsdl(src.getWsdl());
- dest.setDependencies(src.getDependencies());
- dest.setResult(src.getResult());
- return dest;
- }
- }
请注意ValidationTask的call()方法,这里会先检查节点是否被锁定,如果被锁定则表示当前有另一个线程正在验证该节点,那就不用重复进行验证。第50行和第51行,那到锁后立即释放,这里只是为了等待验证结束。
讲到Lock,就不能不讲Conditon,前者代替了synchronized,而后者则代替了Object对象上的wait()、notify()和notifyAll()方法(Condition中提供了await()、signal()和signalAll()方法),当满足运行条件前挂起线程。Condition是与Lock结合使用的,通过Lock.newCondition()方法能够创建与Lock绑定的Condition实例。JDK的JavaDoc中有一个例子能够很好地说明Condition的用途及用法:
- class BoundedBuffer {
- final Lock lock = new ReentrantLock();
- final Condition notFull = lock.newCondition();
- final Condition notEmpty = lock.newCondition();
- final Object[] items = new Object[ 100 ];
- int putptr, takeptr, count;
- public void put(Object x) throws InterruptedException {
- lock.lock();
- try {
- while (count == items.length)
- notFull.await();
- items[putptr] = x;
- if (++putptr == items.length) putptr = 0 ;
- ++count;
- notEmpty.signal();
- } finally {
- lock.unlock();
- }
- }
- public Object take() throws InterruptedException {
- lock.lock();
- try {
- while (count == 0 )
- notEmpty.await();
- Object x = items[takeptr];
- if (++takeptr == items.length) takeptr = 0 ;
- --count;
- notFull.signal();
- return x;
- } finally {
- lock.unlock();
- }
- }
- }
说到这里,让我解释一下之前的例子里为什么没有选择Condition来等待验证结束。await()方法在调用时当前线程先要获得对应的锁,既然我都拿到锁了,那也就是说验证已经结束了。。。