`
talentluke
  • 浏览: 600861 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

java.util.concurrent

 
阅读更多

当 Java 运行时知识库提供便利、预置的并发性时,为什么还要苦苦挣扎,试图将并发性导入到您的 Collections 类?本系列的下一篇文章将会进一步探讨 java.util.concurrent 名称空间的内容。

  Concurrent Collections 是 Java™ 5 的巨大附加产品,但是在关于注释和泛型的争执中很多 Java 开发人员忽视了它们。此外(或者更老实地说),许多开发人员避免使用这个数据包,因为他们认为它一定很复杂,就像它所要解决的问题一样。

   事实上,java.util.concurrent 包含许多类,能够有效解决普通的并发问题,无需复杂工序。阅读本文,了解 java.util.concurrent 类,比如 CopyOnWriteArrayList 和 BlockingQueue 如何帮助您解决多线程编程的棘手问题。

  1. TimeUnit

  尽管本质上 不是 Collections 类,但 java.util.concurrent.TimeUnit 枚举让代码更易读懂。使用 TimeUnit 将使用您的方法或 API 的开发人员从毫秒的 “暴政” 中解放出来。

   TimeUnit 包括所有时间单位,从 MILLISECONDS 和 MICROSECONDS 到 DAYS 和 HOURS,这就意味着它能够处理一个开发人员所需的几乎所有的时间范围类型。同时,因为在列举上声明了转换方法,在时间加快时,将 HOURS 转换回 MILLISECONDS 甚至变得更容易。

  2. CopyOnWriteArrayList

  创建 数组的全新副本是过于昂贵的操作,无论是从时间上,还是从内存开销上,因此在通常使用中很少考虑;开发人员往往求助于使用同步的 ArrayList。然而,这也是一个成本较高的选择,因为每当您跨集合内容进行迭代时,您就不得不同步所有操作,包括读和写,以此保证一致性。

  这又让成本结构回到这样一个场景:需多读者都在读取 ArrayList,但是几乎没人会去修改它。

   CopyOnWriteArrayList 是个巧妙的小宝贝,能解决这一问题。它的 Javadoc 将 CopyOnWriteArrayList 定义为一个 “ArrayList 的线程安全变体,在这个变体中所有易变操作(添加,设置等)可以通过复制全新的数组来实现”。

  集合从内部将它的内容复制到一个没有修改的新数组,这样读者访问数组内容时就不会产生同步成本(因为他们从来不是在易变数据上操作)。

  本质上讲,CopyOnWriteArrayList 很适合处理 ArrayList 经常让我们失败的这种场景:读取频繁,但很少有写操作的集合,例如 JavaBean 事件的 Listeners。

  3. BlockingQueue

   BlockingQueue 接口表示它是一个 Queue,意思是它的项以先入先出(FIFO)顺序存储。在特定顺序插入的项以相同的顺序检索 — 但是需要附加保证,从空队列检索一个项的任何尝试都会阻塞调用线程,直到这个项准备好被检索。同理,想要将一个项插入到满队列的尝试也会导致阻塞调用线 程,直到队列的存储空间可用。

  BlockingQueue 干净利落地解决了如何将一个线程收集的项“传递”给另一线程用于处理的问题,无需考虑同步问题。Java Tutorial 的 Guarded Blocks 试用版就是一个很好的例子。它构建一个单插槽绑定的缓存,当新的项可用,而且插槽也准备好接受新的项时,使用手动同步和 wait()/notifyAll() 在线程之间发信。(详见 Guarded Blocks 实现。)

 

  尽管 Guarded Blocks 教程中的代码有效,但是它耗时久,混乱,而且也并非完全直观。退回到 Java 平台较早的时候,没错,Java 开发人员不得不纠缠于这种代码;但现在是 2010 年 — 情况难道没有改善?

  清单 1 显示了 Guarded Blocks 代码的重写版,其中我使用了一个 ArrayBlockingQueue,而不是手写的 Drop。

  清单 1. BlockingQueue

import java.util. * ;
import java.util.concurrent.
* ;

class Producer
    implements Runnable
{
    
private BlockingQueue < String > drop;
    List
< String > messages = Arrays.asList(
        
" Mares eat oats " ,
        
" Does eat oats " ,
        
" Little lambs eat ivy " ,
        
" Wouldn't you eat ivy too? " );
        
    
public Producer(BlockingQueue < String > d) { this.drop = d; }
    
    
public void run()
    {
        try
        {
            
for ( String s : messages)
                drop.put(s);
            drop.put(
" DONE " );
        }
        catch (InterruptedException intEx)
        {
            System.out.println(
" Interrupted! " +
                
" Last one out, turn out the lights! " );
        }
    }    
}

class Consumer
    implements Runnable
{
    
private BlockingQueue < String > drop;
    
public Consumer(BlockingQueue < String > d) { this.drop = d; }
    
    
public void run()
    {
        try
        {
            
String msg = null ;
            
while (!((msg = drop.take()).equals( " DONE " )))
                System.out.println(msg);
        }
        catch (InterruptedException intEx)
        {
            System.out.println(
" Interrupted! " +
                
" Last one out, turn out the lights! " );
        }
    }
}

public class ABQApp
{
    
public static void main( String [] args)
    {
        BlockingQueue
< String > drop = new ArrayBlockingQueue( 1 , true );
        (
new Thread( new Producer(drop))).start();
        (
new Thread( new Consumer(drop))).start();
    }
}

   ArrayBlockingQueue 还体现了“公平” — 意思是它为读取器和编写器提供线程先入先出访问。这种替代方法是一个更有效,但又冒穷尽部分线程风险的政策。(即,允许一些读取器在其他读取器锁定时运行 效率更高,但是您可能会有读取器线程的流持续不断的风险,导致编写器无法进行工作。)

  BlockingQueue 还支持接收时间参数的方法,时间参数表明线程在返回信号故障以插入或者检索有关项之前需要阻塞的时间。这么做会避免非绑定的等待,这对一个生产系统是致命的,因为一个非绑定的等待会很容易导致需要重启的系统挂起。

  4. ConcurrentMap

  Map 有一个微妙的并发 bug,这个 bug 将许多不知情的 Java 开发人员引入歧途。ConcurrentMap 是最容易的解决方案。

   当一个 Map 被从多个线程访问时,通常使用 containsKey() 或者 get() 来查看给定键是否在存储键/值对之前出现。但是即使有一个同步的 Map,线程还是可以在这个过程中潜入,然后夺取对 Map 的控制权。问题是,在对 put() 的调用中,锁在 get() 开始时获取,然后在可以再次获取锁之前释放。它的结果是个竞争条件:这是两个线程之间的竞争,结果也会因谁先运行而不同。

  如果两个线程 几乎同时调用一个方法,两者都会进行测试,调用 put,在处理中丢失第一线程的值。幸运的是,ConcurrentMap 接口支持许多附加方法,它们设计用于在一个锁下进行两个任务:putIfAbsent(),例如,首先进行测试,然后仅当键没有存储在 Map 中时进行 put。

 

  实现代码看起来几乎相同,但是应用程序有额外获益:SynchronousQueue 允许在队列进行一个插入,只要有一个线程等着使用它。

  在实践中,SynchronousQueue 类似于 Ada 和 CSP 等语言中可用的 “会合通道”。这些通道有时在其他环境中也称为 “连接”,这样的环境包括 .NET (见 参考资料)。

分享到:
评论

相关推荐

    java并发工具包 java.util.concurrent中文版用户指南pdf

    1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    Tomcat内存溢出的解决方法(java.util.concurrent.ExecutionException)

    "java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError" 是一个典型的错误提示,它表明在并发执行过程中遇到了内存不足的问题。下面我们将深入探讨这个问题的原因、影响以及如何解决。 内存溢出...

    java.util.concurrent 学习ppt

    Java.util.concurrent是Java 5.0引入的一个重要包,它为多线程编程提供了一组高级并发工具。这个包的设计者是Doug Lea,它的出现是JSR-166的一部分,也被称作Tiger更新。Java.util.concurrent的引入是为了解决传统...

    java并发工具包 java.util.concurrent中文版pdf

    import java.util.concurrent.ArrayBlockingQueue; public class BlockingQueueExample { public static void main(String[] args) { // 创建一个容量为 10 的阻塞队列 BlockingQueue&lt;String&gt; queue = new ...

    java.util.concurrent系列文章(1)

    ### Java.util.concurrent 系列文章知识点总结 #### 一、引言 随着多核处理器的普及,多线程编程已成为现代软件开发中的一个重要组成部分。Java 5 引入了 `java.util.concurrent` 包,该包提供了丰富的 API 来简化...

    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用

    "JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用" JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用是Java多线程编程中的一种重要概念。随着多线程编程的普及,线程池的使用变得...

    Java并发工具包java.util.concurrent用户指南中英文对照阅读版.pdf

    java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    The java.util.concurrent Synchronizer Framework

    ### Java.util.concurrent.Synchronizer框架详解 #### 一、引言与背景 随着Java技术的发展,多线程编程成为了一项重要的技术需求。为了更好地支持并发编程,Java平台在J2SE 1.5版本中引入了`java.util.concurrent`...

    Java高性能线程库(java.util.concurrent包的补充)

    一个高性能的Java线程库,该库是 JDK 1.5 中的 java.util.concurrent 包的补充,可用于基于并发消息机制的应用。该类库不提供远程的消息功能,其设计的宗旨是实现一个内存中的消息传递机制. 主要特点有: * All ...

    The java. util. concurrent synchronizer framework.pdf

    AQS(AbstractQueuedSynchronizer)是Java.util.concurrent包中同步器的基础框架,它的核心设计思想与实现方法在Doug Lea先生的这篇论文中有详细的介绍。论文详细阐述了AQS框架的原理、设计、实现、应用以及性能等...

    java.util.concurrent.uml.pdf

    标题中提到了“java.util.concurrent.uml.pdf”,这表明文件是一份Java并发编程工具包java.util.concurrent的UML(统一建模语言)类结构图的PDF格式文件。UML图能够帮助开发者理解Java并发包中的类、接口及其关系,...

    java.util.concurrent-多线程框架.docx

    java.util.concurrent 多线程框架 java.util.concurrent 多线程框架是 Java 语言中用于多线程编程的库。该库提供了多种线程池实现、并发集合、同步器、lock 等多种机制,以便开发者更方便地编写高效、可靠的多线程...

    动画学习 java.util.concurrent并发工具包

    如何启动:以win7系统为例,最好jdk8 1.打开cmd,cd到jdk的path,本机是:cd C:\Java\jdk6\bin ...java -cp D:\javaConcurrentAnimated.jar vgrazi.concurrent.samples.launcher.ConcurrentExampleLauncher

    The java.util.concurrent synchronizer framework.pdf

    文档标题“java.util.concurrent同步器框架”和描述“Doug Lea的java.util.concurrent同步器框架”表明本文将探讨由Doug Lea所撰写的关于Java并发编程中同步器框架的内容。文档中提到了AbstractQueuedSynchronizer类...

    java.util.concurrent_您不知道的5件事

    ### Java.util.concurrent_您不知道的5件事 #### 1. Semaphore(信号量) - **定义与作用**:`Semaphore` 类是一种控制多个线程访问共享资源的机制,它通过内部维护一个整数计数器(许可的数量)以及一组等待线程...

    java.util.concurrent.ArrayBlockingQueueAPI介绍和简单案例

    java.util.concurrent.ArrayBlockingQueueAPI介绍和简单案例

    java.util.concurrent 实现线程池队列

    在Java中,`java.util.concurrent.ExecutorService` 接口代表了一个线程池服务,而`ThreadPoolExecutor` 是它的具体实现,我们可以自定义线程池的核心参数,如核心线程数、最大线程数、线程存活时间、线程队列等。...

    java.util.concurrent.BlockingDeque API介绍和简单案例

    java.util.concurrent.BlockingDeque API介绍和简单案例

Global site tag (gtag.js) - Google Analytics