在多核时代,为了提高CPU的使用率同时提高程序的运行效率,并发编程成为一种趋势。JDK1.5(2004-10发布)加入并发包已经过去了5个 年头了,很多java程序员对并发编程仍然一无所知;尤其是在简历上写着精通JAVA的同学们,当我问juc包的使用情况的时候,他们否定使用过,我对他 们的看法也就直接减分了。以前在看《Java 并发编程实践》这边书的时候做过相关API的总结,拿出来扫盲了。

 

    JAVA并发框架API一览

    执行框程序(Executor):最常见的用法就是用Executors来构造相关的线程池;用CompletionService来分离生产任务和已经完成的任务,生产者 submit 执行的任务。使用者 take 已完成的任务,并按照完成这些任务的顺序处理它们的结果;Callable和Future接口到处都需要用到。

    队列(Queue):在使用生产者消费者模式的时候需要构建出满足自己自身系统需要的队列。

    同步器(Synchronizer)常见的有5种:

  1. 信号量(Semaphore)用于限制可以访问某些资源(物理的或逻辑的)的线程数目;
  2. 闭锁(CountDownLatch)是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。用于在保持给定数目的 信号、事件或条件前阻塞执行。把信号量设置为1的时候就是一个闭锁的实现。
  3. 关卡(CyclicBarrier)允许一个给定数量的成员多次集中在一个关卡点,然后在统一执行后面的操作,这在并行迭代算法中非常有用,能把一个问题拆分成一系列相互独立的子问题。
  4. 交换器(Exchanger)是关卡的另一种形式,它提供了一个同步点,在这个同步点,一对线程可以交换数据。每个线程通过exchange()方法的入口提供数据给他的伙伴线程,并接收他的伙伴线程提供的数据,并返回。它在多流水线设计中是有用的。
  5. Future和FutureTask:可取消的异步计算,利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法。FutureTask可以通过线程池来执行。

    并发容器:ConcurrentHashMap来获取的完全并发和更新的所期望可调整并发的哈希表;期望的读数和遍历远远大于列表的更新数时,请使用CopyOnWriteArrayList 代替 ArrayList。

    原子操作(atomic):支持在单个变量上解除锁的线程安全编程;我用的最多的是在多线程中的计数操作。

    互斥锁(Lock):为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器,该框架允许更灵活地使用锁和条件。

 

    Amdahl定律

    Amdahl定律描述了在一个系统中,基于可并行化和串行化的组件各自所占的比重,程序通过获得额外的计算资源,理论上能够加速多少。如果F是必须串行化执行的比重,那么Amdahl定律告诉我们,在一个N处理器的机器中,我们最多可以加速:1/(F+(1-F)/N)
    当N无限增大趋近无穷时,speedup的最大值无限趋近1/F,这意味着一个程序中如果50%的处理都需要串行进行的话,speedup只能提升2倍 (不考虑事实上有多少线程可用);如果程序的10%需要串行进行,speedup最多能够提高近10倍。更多信息点击
这里

 

    Spring中的多线程任务

    在中小型项目中把一些提醒类邮件(邮件发送等级较低)放到spring线程池内执行,可以减少邮件发送等待时间。前提是:这些邮件的发送不需要放到数据库 或者是文件系统中持久化。在邮件发送类中引用:TaskExecutor接口来执行邮件发送的线程,spring的配置使用示例:

<bean id="emailExecutor"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="6" />
        <property name="maxPoolSize" value="10" />
        <property name="queueCapacity" value="80" />
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
        </property>
</bean>