java.util.concurrent包

转自:http://blog.csdn.net/heyutao007/article/details/5672804

讲到Java多线程,大多数人脑海中跳出来的是Thread、Runnable、synchronized……这些是最基本的东西,虽然已经足够强大,但想要用好还真不容易。从JDK1.5开始,增加了java.util.concurrent包,它的引入大大简化了多线程程序的开发(要感谢一下大牛Doug Lee)。 

java.util.concurrent包分成了三个部分,分别是java.util.concurrent、java.util.concurrent.atomic和java.util.concurrent.lock。内容涵盖了并发集合类、线程池机制、同步互斥机制、线程安全的变量更新工具类、锁等等常用工具。 

为了便于理解,本文使用一个例子来做说明,交代一下它的场景:

假设要对一套10个节点组成的环境进行检查,这个环境有两个入口点,通过节点间的依赖关系可以遍历到整个环境。依赖关系可以构成一张有向图,可能存在环。为了提高检查的效率,考虑使用多线程。 

1、Executors

通过这个类能够获得多种线程池的实例,例如可以调用newSingleThreadExecutor()获得单线程的ExecutorService,调用newFixedThreadPool()获得固定大小线程池的ExecutorService。拿到ExecutorService可以做的事情就比较多了,最简单的是用它来执行Runnable对象,也可以执行一些实现了Callable<T>的对象。用Thread的start()方法没有返回值,如果该线程执行的方法有返回值那用ExecutorService就再好不过了,可以选择submit()、invokeAll()或者invokeAny(),根据具体情况选择合适的方法即可。

[java]  view plain copy
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. import java.util.concurrent.ExecutionException;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6. import java.util.concurrent.Future;  
  7. import java.util.concurrent.TimeUnit;  
  8.   
  9. /** 
  10.  * 线程池服务类 
  11.  *  
  12.  * @author DigitalSonic 
  13.  */  
  14. public class ThreadPoolService {  
  15.     /** 
  16.      * 默认线程池大小 
  17.      */  
  18.     public static final int  DEFAULT_POOL_SIZE    = 5;  
  19.   
  20.     /** 
  21.      * 默认一个任务的超时时间,单位为毫秒 
  22.      */  
  23.     public static final long DEFAULT_TASK_TIMEOUT = 1000;  
  24.   
  25.     private int              poolSize             = DEFAULT_POOL_SIZE;  
  26.     private ExecutorService  executorService;  
  27.   
  28.     /** 
  29.      * 根据给定大小创建线程池 
  30.      */  
  31.     public ThreadPoolService(int poolSize) {  
  32.         setPoolSize(poolSize);  
  33.     }  
  34.   
  35.     /** 
  36.      * 使用线程池中的线程来执行任务 
  37.      */  
  38.     public void execute(Runnable task) {  
  39.         executorService.execute(task);  
  40.     }  
  41.   
  42.     /** 
  43.      * 在线程池中执行所有给定的任务并取回运行结果,使用默认超时时间 
  44.      *  
  45.      * @see #invokeAll(List, long) 
  46.      */  
  47.     public List<Node> invokeAll(List<ValidationTask> tasks) {  
  48.         return invokeAll(tasks, DEFAULT_TASK_TIMEOUT * tasks.size());  
  49.     }  
  50.   
  51.     /** 
  52.      * 在线程池中执行所有给定的任务并取回运行结果 
  53.      *  
  54.      * @param timeout 以毫秒为单位的超时时间,小于0表示不设定超时 
  55.      * @see java.util.concurrent.ExecutorService#invokeAll(java.util.Collection) 
  56.      */  
  57.     public List<Node> invokeAll(List<ValidationTask> tasks, long timeout) {  
  58.         List<Node> nodes = new ArrayList<Node>(tasks.size());  
  59.         try {  
  60.             List<Future<Node>> futures = null;  
  61.             if (timeout < 0) {  
  62.                 futures = executorService.invokeAll(tasks);  
  63.             } else {  
  64.                 futures = executorService.invokeAll(tasks, timeout, TimeUnit.MILLISECONDS);  
  65.             }  
  66.             for (Future<Node> future : futures) {  
  67.                 try {  
  68.                     nodes.add(future.get());  
  69.                 } catch (ExecutionException e) {  
  70.                     e.printStackTrace();  
  71.                 }  
  72.             }  
  73.         } catch (InterruptedException e) {  
  74.             e.printStackTrace();  
  75.         }  
  76.         return nodes;  
  77.     }  
  78.   
  79.     /** 
  80.      * 关闭当前ExecutorService 
  81.      *  
  82.      * @param timeout 以毫秒为单位的超时时间 
  83.      */  
  84.     public void destoryExecutorService(long timeout) {  
  85.         if (executorService != null && !executorService.isShutdown()) {  
  86.             try {  
  87.                 executorService.awaitTermination(timeout, TimeUnit.MILLISECONDS);  
  88.             } catch (InterruptedException e) {  
  89.                 e.printStackTrace();  
  90.             }  
  91.             executorService.shutdown();  
  92.         }  
  93.     }  
  94.   
  95.     /** 
  96.      * 关闭当前ExecutorService,随后根据poolSize创建新的ExecutorService 
  97.      */  
  98.     public void createExecutorService() {  
  99.         destoryExecutorService(1000);  
  100.         executorService = Executors.newFixedThreadPool(poolSize);  
  101.     }  
  102.   
  103.     /** 
  104.      * 调整线程池大小 
  105.      * @see #createExecutorService() 
  106.      */  
  107.     public void setPoolSize(int poolSize) {  
  108.         this.poolSize = poolSize;  
  109.         createExecutorService();  
  110.     }  
  111. }  
invokeAll()和invokeAny()方法。前者会执行给定的所有Callable<T>对象,等所有任务完成后返回一个包含了执行结果的List<Future<T>>,每个Future.isDone()都是true,可以用Future.get()拿到结果;后者只要完成了列表中的任意一个任务就立刻返回,返回值就是执行结果。

和其他资源一样,线程池在使用完毕后也需要释放,用shutdown()方法可以关闭线程池,如果当时池里还有没有被执行的任务,它会等待任务执行完毕,在等待期间试图进入线程池的任务将被拒绝。也可以用shutdownNow()来关闭线程池,它会立刻关闭线程池,没有执行的任务作为返回值返回。 

2、Lock

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

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

[java]  view plain copy
  1. Lock l = ...;   
  2. l.lock();  
  3. try {  
  4.     // 执行操作  
  5. finally {  
  6.     l.unlock();  
  7. }  

 java.util.concurrent.locks中提供了几个Lock接口的实现类,比较常用的应该是ReentrantLock。

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

[java]  view plain copy
  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.  }  

3、并发集合类

java.util包中的集合类有的是线程安全的,有的则不是,在编写多线程的程序时使用线程安全的类能省去很多麻烦,但这些类的性能如何呢?java.util.concurrent包中提供了几个并发结合类,例如ConcurrentHashMap、ConcurrentLinkedQueue和CopyOnWriteArrayList等等,根据不同的使用场景,开发者可以用它们替换java.util包中的相应集合类。

CopyOnWriteArrayList是ArrayList的一个变体,比较适合用在读取比较频繁、修改较少的情况下,因为每次修改都要复制整个底层数组。ConcurrentHashMap中为Map接口增加了一些方法(例如putIfAbsenct()),同时做了些优化,下面的代码中使用ConcurrentHashMap来作为全局节点表,完全无需考虑并发问题。

[java]  view plain copy
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. import java.util.Map;  
  4. import java.util.concurrent.ConcurrentHashMap;  
  5.   
  6. /** 
  7.  * 执行验证的服务类 
  8.  *  
  9.  * @author DigitalSonic 
  10.  */  
  11. public class ValidationService {  
  12.     /** 
  13.      * 全局节点表 
  14.      */  
  15.     public static final Map<String, Node> NODE_MAP = new ConcurrentHashMap<String, Node>();  
  16.   
  17.     private ThreadPoolService threadPoolService;  
  18.       
  19.     public ValidationService(ThreadPoolService threadPoolService) {  
  20.         this.threadPoolService = threadPoolService;  
  21.     }  
  22.   
  23.     /** 
  24.      * 给出一个入口节点的WSDL,通过广度遍历的方式验证与其相关的各个节点 
  25.      *  
  26.      * @param wsdl 入口节点WSDL 
  27.      */  
  28.     public void validate(List<String> wsdl) {  
  29.         List<String> visitedNodes = new ArrayList<String>();  
  30.         List<String> nextRoundNodes = new ArrayList<String>();  
  31.   
  32.         nextRoundNodes.addAll(wsdl);  
  33.         while (nextRoundNodes.size() > 0) {  
  34.             List<ValidationTask> tasks = getTasks(nextRoundNodes);  
  35.             List<Node> nodes = threadPoolService.invokeAll(tasks);  
  36.   
  37.             visitedNodes.addAll(nextRoundNodes);  
  38.             nextRoundNodes.clear();  
  39.             getNextRoundNodes(nodes, visitedNodes, nextRoundNodes);  
  40.         }  
  41.     }  
  42.   
  43.     private List<String> getNextRoundNodes(List<Node> nodes,  
  44.             List<String> visitedNodes, List<String> nextRoundNodes) {  
  45.         for (Node node : nodes) {  
  46.             for (String wsdl : node.getDependencies()) {  
  47.                 if (!visitedNodes.contains(wsdl)) {  
  48.                     nextRoundNodes.add(wsdl);  
  49.                 }  
  50.             }  
  51.         }  
  52.         return nextRoundNodes;  
  53.     }  
  54.   
  55.     private List<ValidationTask> getTasks(List<String> nodes) {  
  56.         List<ValidationTask> tasks = new ArrayList<ValidationTask>(nodes.size());  
  57.         for (String wsdl : nodes) {  
  58.             tasks.add(new ValidationTask(wsdl));  
  59.         }  
  60.         return tasks;  
  61.     }  
  62. }  

4、AtomicInteger

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

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


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值