实战Concurrent(2)

来自http://www.iteye.com/topic/363625

 

2、Lock

多线程编程中常常要锁定某个对象,之前会用synchronized来实现,现在又多了另一种选择,那就是java.util.concurrent.locks。通过Lock能够实现更灵活的锁定机制,它还提供了很多synchronized所没有的功能,例如尝试获得锁(tryLock())。

 

使用Lock时需要自己获得锁并在使用后手动释放,这一点与synchronized有所不同,所以通常Lock的使用方式是这样的:

Java代码  
  1. Lock l = ...;   
  2. l.lock();  
  3. try  {  
  4.     // 执行操作   
  5. finally  {  
  6.     l.unlock();  
  7. }  

 

java.util.concurrent.locks中提供了几个Lock接口的实现类,比较常用的应该是ReentrantLock。以下范例中使用了ReentrantLock进行节点锁定:

Java代码  
  1. package  service;  
  2.   
  3. import  java.util.concurrent.locks.Lock;  
  4. import  java.util.concurrent.locks.ReentrantLock;  
  5.   
  6. /**  
  7.  * 节点类  
  8.  *   
  9.  * @author DigitalSonic  
  10.  */   
  11. public   class  Node {  
  12.     private  String name;  
  13.     private  String wsdl;  
  14.     private  String result =  "PASS" ;  
  15.     private  String[] dependencies =  new  String[] {};  
  16.     private  Lock lock =  new  ReentrantLock();  
  17.     /**  
  18.      * 默认构造方法  
  19.      */   
  20.     public  Node() {  
  21.     }  
  22.       
  23.     /**  
  24.      * 构造节点对象,设置名称及WSDL  
  25.      */   
  26.     public  Node(String name, String wsdl) {  
  27.         this .name = name;  
  28.         this .wsdl = wsdl;  
  29.     }  
  30.   
  31.     /**  
  32.      * 返回包含节点名称、WSDL以及验证结果的字符串  
  33.      */   
  34.     @Override   
  35.     public  String toString() {  
  36.         String toString = "Node: "  + name +  " WSDL: "  + wsdl +  " Result: "  + result;  
  37.         return  toString;  
  38.     }  
  39.       
  40.     // Getter & Setter   
  41.     ...
  42.   
  43. }  
Java代码  
  1. package  service;  
  2.   
  3. import  java.util.concurrent.Callable;  
  4. import  java.util.concurrent.locks.Lock;  
  5. import  java.util.logging.Logger;  
  6.   
  7. import  service.mock.MockNodeValidator;  
  8.   
  9. /**  
  10.  * 执行验证的任务类  
  11.  *   
  12.  * @author DigitalSonic  
  13.  */   
  14. public   class  ValidationTask  implements  Callable<Node> {  
  15.     private   static  Logger logger = Logger.getLogger( "ValidationTask" );  
  16.   
  17.     private  String        wsdl;  
  18.   
  19.     /**  
  20.      * 构造方法,传入节点的WSDL  
  21.      */   
  22.     public  ValidationTask(String wsdl) {  
  23.         this .wsdl = wsdl;  
  24.     }  
  25.   
  26.     /**  
  27.      * 执行针对某个节点的验证<br/>  
  28.      * 如果正有别的线程在执行同一节点的验证则等待其结果,不重复执行验证  
  29.      */   
  30.     @Override   
  31.     public  Node call()  throws  Exception {  
  32.         Node node = ValidationService.NODE_MAP.get(wsdl);  
  33.         Lock lock = null ;  
  34.         logger.info("开始验证节点:"  + wsdl);  
  35.         if  (node !=  null ) {  
  36.             lock = node.getLock();  
  37.             if  (lock.tryLock()) {  
  38.                 // 当前没有其他线程验证该节点   
  39.                 logger.info("当前没有其他线程验证节点"  + node.getName() +  "["  + wsdl +  "]" );  
  40.                 try  {  
  41.                     Node result = MockNodeValidator.validateNode(wsdl);  
  42.                     mergeNode(result, node);  
  43.                 } finally  {  
  44.                     lock.unlock();  
  45.                 }  
  46.             } else  {  
  47.                 // 当前有别的线程正在验证该节点,等待结果   
  48.                 logger.info("当前有别的线程正在验证节点"  + node.getName() +  "["  + wsdl +  "],等待结果" );  
  49.                 lock.lock();  
  50.                 lock.unlock();  
  51.             }  
  52.         } else  {  
  53.             // 从未进行过验证,这种情况应该只出现在系统启动初期   
  54.             // 这时是在做初始化,不应该有冲突发生   
  55.             logger.info("首次验证节点:"  + wsdl);  
  56.             node = MockNodeValidator.validateNode(wsdl);  
  57.             ValidationService.NODE_MAP.put(wsdl, node);  
  58.         }  
  59.         logger.info("节点"  + node.getName() +  "["  + wsdl +  "]验证结束,验证结果:"  + node.getResult());  
  60.         return  node;  
  61.     }  
  62.   
  63.     /**  
  64.      * 将src的内容合并进dest节点中,不进行深度拷贝  
  65.      */   
  66.     private  Node mergeNode(Node src, Node dest) {  
  67.         dest.setName(src.getName());  
  68.         dest.setWsdl(src.getWsdl());  
  69.         dest.setDependencies(src.getDependencies());  
  70.         dest.setResult(src.getResult());  
  71.         return  dest;  
  72.     }  
  73. }  
   

请注意ValidationTask的call()方法,这里会先检查节点是否被锁定,如果被锁定则表示当前有另一个线程正在验证该节点,那就不用重复进行验证。第50行和第51行,那到锁后立即释放,这里只是为了等待验证结束。

 

讲到Lock,就不能不讲Conditon,前者代替了synchronized,而后者则代替了Object对象上的wait()、notify()和notifyAll()方法(Condition中提供了await()、signal()和signalAll()方法),当满足运行条件前挂起线程。Condition是与Lock结合使用的,通过Lock.newCondition()方法能够创建与Lock绑定的Condition实例。JDK的JavaDoc中有一个例子能够很好地说明Condition的用途及用法:

Java代码  
  1. class  BoundedBuffer {  
  2.   final  Lock lock =  new  ReentrantLock();  
  3.   final  Condition notFull  = lock.newCondition();   
  4.   final  Condition notEmpty = lock.newCondition();   
  5.   
  6.   final  Object[] items =  new  Object[ 100 ];  
  7.   int  putptr, takeptr, count;  
  8.   
  9.   public   void  put(Object x)  throws  InterruptedException {  
  10.     lock.lock();  
  11.     try  {  
  12.       while  (count == items.length)   
  13.         notFull.await();  
  14.       items[putptr] = x;   
  15.       if  (++putptr == items.length) putptr =  0 ;  
  16.       ++count;  
  17.       notEmpty.signal();  
  18.     } finally  {  
  19.       lock.unlock();  
  20.     }  
  21.   }  
  22.   
  23.   public  Object take()  throws  InterruptedException {  
  24.     lock.lock();  
  25.     try  {  
  26.       while  (count ==  0 )   
  27.         notEmpty.await();  
  28.       Object x = items[takeptr];   
  29.       if  (++takeptr == items.length) takeptr =  0 ;  
  30.       --count;  
  31.       notFull.signal();  
  32.       return  x;  
  33.     } finally  {  
  34.       lock.unlock();  
  35.     }  
  36.   }   
  37. }  

说到这里,让我解释一下之前的例子里为什么没有选择Condition来等待验证结束。await()方法在调用时当前线程先要获得对应的锁,既然我都拿到锁了,那也就是说验证已经结束了。。。

你可能感兴趣的:(jdk,多线程,编程)