Effective Java读书笔记二四(Java Tips.Day.24)

来源:互联网 发布:淘宝购物卡有什么用 编辑:程序博客网 时间:2024/05/29 19:00

TIP 68 executor 和 task 优先于线程


使用Java 1.5提供的Executor Framework,代替Thread来执行并发任务。


  1. 如果执行多线程任务,还可以开启缓存线程池。
  2. 如果负载较重,可以开启固定线程数的线程池。
  3. 尽量不直接使用线程,使用executor service 执行任务。
  4. 可以使用 ScheduledExecutorService代替 java.util.Timer 来执行周期任务。

TIP 69 并发工具优先于wait和notify


现在已经不建议使用 wait 和notify 了, 因为要正确地使用wait 和notify 比较困难,那就用更高级的并发工具来代替它们。


java.util.concurrent 中更高级的工具分为三类:

  • Executor Framework
  • Concurrent Collection 并发集合
  • Synchronized 同步器

并发集合

为标准的集合接口提供了高性能的并发实现。

  • 使用并发集合,代替老式的同步集合。比如,应该优先使用 ConcurrentMap ,而不是 Collections.synchronizedMap ,性能可以获得极大的提高。
  • 有些集合已经通过阻塞操作进行了扩展,它们会一直等待(或者阻塞)到可以执行为止。大多数ExecutorService 实现都使用BlockingQueue。

同步器

常用的是CountDown Latch 和 Semaphore,而 CyclicBarrier 和 Exchanger 用的比较少。

CountDown Latch是一次性的障碍,允许一个或多个线程等待一个或多个线程来做某些事情。

  • 对于间歇性的定时,时钟应该优先使用System.nanoTime,而不是System.currentTimeMills。System.nanoTime更加精确,不受系统的实时时钟的影响。

  • 你也有可能需要维护使用了wait和notify的遗留代码。wait方法被用来使线程等待某个条件,它必须在同步区域内被调用,这个同步区域将对象锁定在了调用wait方法的对象上 :

synchronized (obj){     while (<condition does not hold>){         obj.wait();      }}
  • 始终应该使用wait循环模式来调用wait方法:永远不要在循环之外调用wait方法,循环会在等待之前和之后测试条件。

TIP 70 线程安全性的文档花


一个类为了可以被多个线程安全地使用,必须在文档中清楚地说明它所支持的线程安全级别。


常见的几种线程安全级别:

  • 不可变的(immutable) —— 这个类的实例是不变的,所以,不需要外部的同步,比如BigInteger, String , 和 Long。
  • 无条件的线程安全(unconditionally thread-safe) —— 这个类的实例是可变的,但是这个类有着足够的内部同步,所以它的实例可以被并发使用,无需外部同步。比如Random和ConcurrentHashMap。
  • 有条件的线程安全(conditionally thread-safe) —— 除了有些方法为进行安全的并发使用而需要外部同步之外,这种线程安全级别与无条件的线程安全相同。这样的例子包含Collections.synchronized包装返回的集合,它们的迭代器要求外部同步。
  • 非线程安全 (not thread-safe) ——- 这个类的实例是可变的。为了并发的使用它们,客户必须利用自己选择的外部同步包围每个方法调用或调用序列。这样的例子包含通用的集合实现。
  • 线程对立的 (thread-hostile) —— 这个类不能安全地被多个线程并发使用,即使所有的方法调用都被外部同步包围。线程对立的根源通常在于:没有同步地修改静态数据。在JDK中,线程对立的类或者方法极少,几乎没有。

简而言之,每个类都应该准确、详尽地在注解中说明线程安全属性。
synchronized修饰符与这个文档毫无关系。

有条件的线程安全类必须在文档中指明:哪些方法需要外部同步,以及在执行这些序列时要获得哪把锁。

如果你编写的是无条件的类型安全类,就应该考虑使用私有锁对象来代替同步的方法。