实战Concurrent(续)

本文通过具体实例介绍了Java并发编程中AtomicInteger和CountDownLatch的应用,演示了如何利用这些工具类解决多线程环境下的计数和等待问题。

4、AtomicInteger

对变量的读写操作都是原子操作(除了long或者double的变量),但像数值类型的++ --操作不是原子操作,像i++中包含了获得i的原始值、加1、写回i、返回原始值,在进行类似i++这样的操作时如果不进行同步问题就大了。好在java.util.concurrent.atomic为我们提供了很多工具类,可以以原子方式更新变量。

 

以AtomicInteger为例,提供了代替++ --的getAndIncrement()、incrementAndGet()、getAndDecrement()和decrementAndGet()方法,还有加减给定值的方法、当前值等于预期值时更新的compareAndSet()方法。

 

下面的例子中用AtomicInteger保存全局验证次数(第69行做了自增的操作),因为validateNode()方法会同时被多个线程调用,所以直接用int不同步是不行的,但用AtomicInteger在这种场合下就很合适。

Java代码 复制代码
  1. package service.mock;   
  2.   
  3. import java.util.ArrayList;   
  4. import java.util.HashMap;   
  5. import java.util.List;   
  6. import java.util.Map;   
  7. import java.util.concurrent.atomic.AtomicInteger;   
  8. import java.util.logging.Logger;   
  9.   
  10. import service.Node;   
  11.   
  12. /**  
  13.  * 模拟执行节点验证的Mock类  
  14.  *   
  15.  * @author DigitalSonic  
  16.  */  
  17. public class MockNodeValidator {   
  18.     public static final List<Node>         ENTRIES  = new ArrayList<Node>();   
  19.     private static final Map<String, Node> NODE_MAP = new HashMap<String, Node>();   
  20.   
  21.     private static AtomicInteger           count    = new AtomicInteger(0);   
  22.     private static Logger                  logger   = Logger.getLogger("MockNodeValidator");   
  23.   
  24.     /*  
  25.      * 构造模拟数据  
  26.      */  
  27.     static {   
  28.         Node node0 = new Node("NODE0""http://node0/check?wsdl"); //入口0  
  29.         Node node1 = new Node("NODE1""http://node1/check?wsdl");   
  30.         Node node2 = new Node("NODE2""http://node2/check?wsdl");   
  31.         Node node3 = new Node("NODE3""http://node3/check?wsdl");   
  32.         Node node4 = new Node("NODE4""http://node4/check?wsdl");   
  33.         Node node5 = new Node("NODE5""http://node5/check?wsdl");   
  34.         Node node6 = new Node("NODE6""http://node6/check?wsdl"); //入口1  
  35.         Node node7 = new Node("NODE7""http://node7/check?wsdl");   
  36.         Node node8 = new Node("NODE8""http://node8/check?wsdl");   
  37.         Node node9 = new Node("NODE9""http://node9/check?wsdl");   
  38.   
  39.         node0.setDependencies(new String[] { node1.getWsdl(), node2.getWsdl() });   
  40.         node1.setDependencies(new String[] { node3.getWsdl(), node4.getWsdl() });   
  41.         node2.setDependencies(new String[] { node5.getWsdl() });   
  42.         node6.setDependencies(new String[] { node7.getWsdl(), node8.getWsdl() });   
  43.         node7.setDependencies(new String[] { node5.getWsdl(), node9.getWsdl() });   
  44.         node8.setDependencies(new String[] { node3.getWsdl(), node4.getWsdl() });   
  45.   
  46.         node2.setResult("FAILED");   
  47.   
  48.         NODE_MAP.put(node0.getWsdl(), node0);   
  49.         NODE_MAP.put(node1.getWsdl(), node1);   
  50.         NODE_MAP.put(node2.getWsdl(), node2);   
  51.         NODE_MAP.put(node3.getWsdl(), node3);   
  52.         NODE_MAP.put(node4.getWsdl(), node4);   
  53.         NODE_MAP.put(node5.getWsdl(), node5);   
  54.         NODE_MAP.put(node6.getWsdl(), node6);   
  55.         NODE_MAP.put(node7.getWsdl(), node7);   
  56.         NODE_MAP.put(node8.getWsdl(), node8);   
  57.         NODE_MAP.put(node9.getWsdl(), node9);   
  58.   
  59.         ENTRIES.add(node0);   
  60.         ENTRIES.add(node6);   
  61.     }   
  62.   
  63.     /**  
  64.      * 模拟执行远程验证返回节点,每次调用等待500ms  
  65.      */  
  66.     public static Node validateNode(String wsdl) {   
  67.         Node node = cloneNode(NODE_MAP.get(wsdl));   
  68.         logger.info("验证节点" + node.getName() + "[" + node.getWsdl() + "]");   
  69.         count.getAndIncrement();   
  70.         try {   
  71.             Thread.sleep(500);   
  72.         } catch (InterruptedException e) {   
  73.             e.printStackTrace();   
  74.         }   
  75.         return node;   
  76.     }   
  77.   
  78.     /**  
  79.      * 获得计数器的值  
  80.      */  
  81.     public static int getCount() {   
  82.         return count.intValue();   
  83.     }   
  84.   
  85.     /**  
  86.      * 克隆一个新的Node对象(未执行深度克隆)  
  87.      */  
  88.     public static Node cloneNode(Node originalNode) {   
  89.         Node newNode = new Node();   
  90.   
  91.         newNode.setName(originalNode.getName());   
  92.         newNode.setWsdl(originalNode.getWsdl());   
  93.         newNode.setResult(originalNode.getResult());   
  94.         newNode.setDependencies(originalNode.getDependencies());   
  95.   
  96.         return newNode;   
  97.     }   
  98. }  
package service.mock;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

import service.Node;

/**
 * 模拟执行节点验证的Mock类
 * 
 * @author DigitalSonic
 */
public class MockNodeValidator {
    public static final List<Node>         ENTRIES  = new ArrayList<Node>();
    private static final Map<String, Node> NODE_MAP = new HashMap<String, Node>();

    private static AtomicInteger           count    = new AtomicInteger(0);
    private static Logger                  logger   = Logger.getLogger("MockNodeValidator");

    /*
     * 构造模拟数据
     */
    static {
        Node node0 = new Node("NODE0", "http://node0/check?wsdl"); //入口0
        Node node1 = new Node("NODE1", "http://node1/check?wsdl");
        Node node2 = new Node("NODE2", "http://node2/check?wsdl");
        Node node3 = new Node("NODE3", "http://node3/check?wsdl");
        Node node4 = new Node("NODE4", "http://node4/check?wsdl");
        Node node5 = new Node("NODE5", "http://node5/check?wsdl");
        Node node6 = new Node("NODE6", "http://node6/check?wsdl"); //入口1
        Node node7 = new Node("NODE7", "http://node7/check?wsdl");
        Node node8 = new Node("NODE8", "http://node8/check?wsdl");
        Node node9 = new Node("NODE9", "http://node9/check?wsdl");

        node0.setDependencies(new String[] { node1.getWsdl(), node2.getWsdl() });
        node1.setDependencies(new String[] { node3.getWsdl(), node4.getWsdl() });
        node2.setDependencies(new String[] { node5.getWsdl() });
        node6.setDependencies(new String[] { node7.getWsdl(), node8.getWsdl() });
        node7.setDependencies(new String[] { node5.getWsdl(), node9.getWsdl() });
        node8.setDependencies(new String[] { node3.getWsdl(), node4.getWsdl() });

        node2.setResult("FAILED");

        NODE_MAP.put(node0.getWsdl(), node0);
        NODE_MAP.put(node1.getWsdl(), node1);
        NODE_MAP.put(node2.getWsdl(), node2);
        NODE_MAP.put(node3.getWsdl(), node3);
        NODE_MAP.put(node4.getWsdl(), node4);
        NODE_MAP.put(node5.getWsdl(), node5);
        NODE_MAP.put(node6.getWsdl(), node6);
        NODE_MAP.put(node7.getWsdl(), node7);
        NODE_MAP.put(node8.getWsdl(), node8);
        NODE_MAP.put(node9.getWsdl(), node9);

        ENTRIES.add(node0);
        ENTRIES.add(node6);
    }

    /**
     * 模拟执行远程验证返回节点,每次调用等待500ms
     */
    public static Node validateNode(String wsdl) {
        Node node = cloneNode(NODE_MAP.get(wsdl));
        logger.info("验证节点" + node.getName() + "[" + node.getWsdl() + "]");
        count.getAndIncrement();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return node;
    }

    /**
     * 获得计数器的值
     */
    public static int getCount() {
        return count.intValue();
    }

    /**
     * 克隆一个新的Node对象(未执行深度克隆)
     */
    public static Node cloneNode(Node originalNode) {
        Node newNode = new Node();

        newNode.setName(originalNode.getName());
        newNode.setWsdl(originalNode.getWsdl());
        newNode.setResult(originalNode.getResult());
        newNode.setDependencies(originalNode.getDependencies());

        return newNode;
    }
}

 

上述代码还有另一个功能,就是构造测试用的节点数据,一共10个节点,有2个入口点,通过这两个点能够遍历整个系统。每次调用会模拟远程访问,等待500ms。环境间节点依赖如下:

环境依赖
Node0 [Node1, Node2]
Node1 [Node3, Node4]
Node2 [Node5]
Node6 [Node7, Node8]
Node7 [Node5, Node9]
Node8 [Node3, Node4]

 

5、CountDownLatch

CountDownLatch是一个一次性的同步辅助工具,允许一个或多个线程一直等待,直到计数器值变为0。它有一个构造方法,设定计数器初始值,即在await()结束等待前需要调用多少次countDown()方法。CountDownLatch的计数器不能重置,所以说它是“一次性”的,如果需要重置计数器,可以使用CyclicBarrier。在运行环境检查的主类中,使用了CountDownLatch来等待所有验证结束,在各个并发验证的线程完成任务结束前都会调用countDown(),因为有3个并发的验证,所以将计数器设置为3。

 

最后将所有这些类整合起来,运行环境检查的主类如下。它会创建线程池服务和验证服务,先做一次验证(相当于是对系统做次初始化),随后并发3个验证请求。系统运行完毕会显示实际执行的节点验证次数和执行时间。如果是顺序执行,验证次数应该是13*4=52,但实际的验证次数会少于这个数字(我这里最近一次执行了33次验证),因为如果同时有两个线程要验证同一节点时只会做一次验证。关于时间,如果是顺序执行,52次验证每次等待500ms,那么验证所耗费的时间应该是26000ms,使用了多线程后的实际耗时远小于该数字(最近一次执行耗时4031ms)。

Java代码 复制代码
  1. package service.mock;   
  2.   
  3. import java.util.ArrayList;   
  4. import java.util.List;   
  5. import java.util.concurrent.CountDownLatch;   
  6.   
  7. import service.Node;   
  8. import service.ThreadPoolService;   
  9. import service.ValidationService;   
  10.   
  11. /**  
  12.  * 模拟执行这个环境的验证  
  13.  *   
  14.  * @author DigitalSonic  
  15.  */  
  16. public class ValidationStarter implements Runnable {   
  17.     private List<String>      entries;   
  18.     private ValidationService validationService;   
  19.     private CountDownLatch    signal;   
  20.   
  21.     public ValidationStarter(List<String> entries, ValidationService validationService,   
  22.             CountDownLatch signal) {   
  23.         this.entries = entries;   
  24.         this.validationService = validationService;   
  25.         this.signal = signal;   
  26.     }   
  27.   
  28.     /**  
  29.      * 线程池大小为10,初始化执行一次,随后并发三个验证  
  30.      */  
  31.     public static void main(String[] args) {   
  32.         ThreadPoolService threadPoolService = new ThreadPoolService(10);   
  33.         ValidationService validationService = new ValidationService(threadPoolService);   
  34.         List<String> entries = new ArrayList<String>();   
  35.         CountDownLatch signal = new CountDownLatch(3);   
  36.         long start;   
  37.         long stop;   
  38.   
  39.         for (Node node : MockNodeValidator.ENTRIES) {   
  40.             entries.add(node.getWsdl());   
  41.         }   
  42.   
  43.         start = System.currentTimeMillis();   
  44.   
  45.         validationService.validate(entries);   
  46.         threadPoolService.execute(new ValidationStarter(entries, validationService, signal));   
  47.         threadPoolService.execute(new ValidationStarter(entries, validationService, signal));   
  48.         threadPoolService.execute(new ValidationStarter(entries, validationService, signal));   
  49.   
  50.         try {   
  51.             signal.await();   
  52.         } catch (InterruptedException e) {   
  53.             e.printStackTrace();   
  54.         }   
  55.   
  56.         stop = System.currentTimeMillis();   
  57.         threadPoolService.destoryExecutorService(1000);   
  58.         System.out.println("实际执行验证次数: " + MockNodeValidator.getCount());   
  59.         System.out.println("实际执行时间: " + (stop - start) + "ms");   
  60.     }   
  61.   
  62.     @Override  
  63.     public void run() {   
  64.         validationService.validate(entries);   
  65.         signal.countDown();   
  66.     }   
  67.   
  68. }  
package service.mock;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import service.Node;
import service.ThreadPoolService;
import service.ValidationService;

/**
 * 模拟执行这个环境的验证
 * 
 * @author DigitalSonic
 */
public class ValidationStarter implements Runnable {
    private List<String>      entries;
    private ValidationService validationService;
    private CountDownLatch    signal;

    public ValidationStarter(List<String> entries, ValidationService validationService,
            CountDownLatch signal) {
        this.entries = entries;
        this.validationService = validationService;
        this.signal = signal;
    }

    /**
     * 线程池大小为10,初始化执行一次,随后并发三个验证
     */
    public static void main(String[] args) {
        ThreadPoolService threadPoolService = new ThreadPoolService(10);
        ValidationService validationService = new ValidationService(threadPoolService);
        List<String> entries = new ArrayList<String>();
        CountDownLatch signal = new CountDownLatch(3);
        long start;
        long stop;

        for (Node node : MockNodeValidator.ENTRIES) {
            entries.add(node.getWsdl());
        }

        start = System.currentTimeMillis();

        validationService.validate(entries);
        threadPoolService.execute(new ValidationStarter(entries, validationService, signal));
        threadPoolService.execute(new ValidationStarter(entries, validationService, signal));
        threadPoolService.execute(new ValidationStarter(entries, validationService, signal));

        try {
            signal.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        stop = System.currentTimeMillis();
        threadPoolService.destoryExecutorService(1000);
        System.out.println("实际执行验证次数: " + MockNodeValidator.getCount());
        System.out.println("实际执行时间: " + (stop - start) + "ms");
    }

    @Override
    public void run() {
        validationService.validate(entries);
        signal.countDown();
    }

}

   

=================================我是分割线==============================

 

本文没有覆盖java.util.concurrent中的所有内容,只是挑选一些比较常用的东西,想要获得更多详细信息请阅读JavaDoc。自打有了“轮子”理论,重复造大轮子的情况的确少了,但还是有人会做些小轮子,例如编写多线程程序时用到的小工具(线程池、锁等等),如果可以,请让自己再“懒惰”一点吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值