zoukankan      html  css  js  c++  java
  • Java并发编程(三) 并发类库中的常用类

    1. 同步容器类

      遗留下来的同步容器类包括Vector和Hashtable,此外java.util.Collections类中还提供了以下工厂方法创建线程安全的容器对象:

      Collections.synchronizedList 返回支持同步操作(线程安全)的List对象;

      Collections.synchronizedSet 返回支持同步操作(线程全的)的Set对象;

      Collections.synchronizedMap 返回支持同步操作(线程安全)的Map对象;

      需要注意的是,同步容器类中的每一个单独的方法都是线程安全的,但对这些方法的复合操作仍然需要额外的客户端加锁来保护。另外,同步容器类对象执行每个操作期间都会持有一个锁,这会导致吞吐量大大降低。例如:

    List<String>  synList = Collections.synchronizedList(list);
    
    synchronized(synList)
    {
       for(int i = 0; i < synList.size(); i++)
         System.out.println(synList.get(i));
    }

      对synList对象调用size方法和get方法需要用客户端锁来保护,并且在这个迭代中,需要频繁的加锁和释放锁,吞吐量很低。 使用迭代器对同步容器类对象进行迭代也是需要加锁的,否则在迭代时若有其他线程修改则会抛出ConcurrentModificationException异常。    

    2. 并发容器类

      并发容器类对同步容器类的性能进行了较大的改进:

      (1)并发容器类提供了一些常用的表示复合操作的方法,并能确保这些复合操作是线程安全的。

        例如:ConcurrentHashMap.putIfAbsent方法,相当于:

          if(!map.contains(key))

            map.put(key,value);

      (2)并发容器类可以大幅度提高并发容器类上操作的吞吐量。

      (3)并发容器类的迭代器和普通的迭代器不一样,这些迭代器具有弱一致性,不具有“fast-fail”特性,不会抛出ConcurrentModificationException。

      常见的并发容器类有:

      ConcurrentLinkedQueue表示并发的队列;BlockingQueue表示阻塞队列,增加了可阻塞的入队和出队操作。如果队列为空,则出队操作阻塞,对于有界队列来说,如果队列满了则入队操作阻塞。阻塞队列可以方便地用于实现“生产者-消费者”模式。

      CopyOnWriteArrayList表示并发的线性表,它通过在每个线程对底层List作出修改时复制整个底层List来实现并发的线程安全,这需要很大的开销,但是对于多线程环境下遍历为主要操作的List来说是很有用的。CopyOnWriteArraySet与CopyOnWriteList类似。

      ConcurrentHashMap表示并发的映射表,ConcurrentHashMap并没有像Hashtable和synchronized的Map一样加独占锁,它使用了更细粒度的分段锁机制,可以大幅提高并发的吞吐量。但是有些方法的返回结果是不准确的,如:size方法和isEmpty方法返回的并不是实时的状态。如果在多线程环境中需要对Map对象独占式的访问,就不应该使用ConcurrentHashMap类。

    3. 工作密取

      在“生产者—消费者”模式中,生产者和消费者共享一个队列,而在工作密取的情境中,每个消费者都有一个双端队列,在消费者完成了自己队列中的工作时,可以去其他消费者队列的队尾取来工作,而并不会干扰其他消费者的工作。在工作密取情境中,消费者从自己队列的队头取自己的工作,从其他消费者的队尾取别人的工作来完成。

      工作密取非常适合于消费者同时也是生产者的情形,当消费者执行工作时发现有更多的工作要做,则可以将这些工作放到自己队列的末尾,也可以送到其他消费者队列的队尾;当自己队列没有工作要做时,可以去其他消费者队列取工作来完成,这样每个消费者都会保持忙碌的状态。

      工作密取可以使用Deque(双端队列)来完成,ArrayDeque和LinkedList是非阻塞双端队列的实现;BlockingDeque表示阻塞的双端队列,putFirst、takeFirst、putLast、takeLast等方法实现了阻塞地访问队头或者队尾,LinkedBlockingDeque是阻塞双端队列的实现。

    4. 同步工具类

      同步工具类的作用是根据其自身的状态来协调多线程的运行,同步工具类封装了一些状态,这些状态将决定某些线程是否阻塞,还提供了一些方法对这些状态进行操作。

      常见的同步工具类有:阻塞队列、闭锁、信号量、栅栏。

      (1)闭锁

      闭锁可以延迟线程的进度直到闭锁到达终止状态,可以用来确保某些活动直到其他某个活动都完成后才继续执行。例如:

      确保某个计算在其需要的所有资源都准备好之后才执行;

      确保某个服务在其依赖的所有服务都启动后才启动;

      等待直到某个操作的所有参与者就绪;

      CountDownLatch是常用的闭锁的实现,CountDownLatch包括一个计数器,countDown方法将计数器减1,表示有一个事件发生了,await方法使调用线程阻塞,直到计数器为0,表示等待的所有事件都已经发生。

      FutureTask也可以作为闭锁,FutureTask的计算任务通过Callable来实现,Callable接口有一个call方法,可以返回计算的结果。FutureTask提供了get方法,调用get方法试图获得计算结果,任务已完成则立即获取结果,否则将阻塞直到计算结束。

      (2)信号量

      信号量可以用来控制同时访问某个资源的线程数量,可以用来实现资源池。

      Semaphore是信号量的实现,Semaphore维护着一定数量的许可,在执行操作时可以首先通过acquire方法获得许可,如果许可已经用完则阻塞,操作执行完后通过release方法释放许可。

      许可数目为1的信号量可以作为互斥信号量,并且是不可重入的。

      (3)栅栏

      栅栏和闭锁很相似,主要区别在于栅栏延迟线程的进度直到所有线程都准备好以后。

      CyclicBarrier是栅栏的实现,线程调用await方法到达栅栏处,若该线程不是到达栅栏处的最后一个线程,则被阻塞。若该线程是到达栅栏处的最后一个线程,则不会阻塞,并且栅栏释放,所有的线程都可以继续执行。

      所有线程都释放之后,栅栏将被重置,栅栏非常适合于并行迭代算法。

      

    参考资料 《Java并发编程实战》

  • 相关阅读:
    Hibernate学习之缓存机制
    Hibernate学习之hibernate状态
    Ajax学习之小结
    Hibernate学习之hibernate执行顺序
    Svn入门
    Svn服务启动的两种方式
    Eclipse安装Svn插件
    一种给力的带背景的超链接的写法
    转载:IE下div使用margin:0px auto不居中的原因
    github上的Lua in Erlang
  • 原文地址:https://www.cnblogs.com/jqctop1/p/4777385.html
Copyright © 2011-2022 走看看