《Effective Java》第二版学习笔记之并发编程。
第66条 同步访问共享的可变数据
* 关键字synchronized可以保证在同一时刻只有一个线程可以执行某个方法或代码块。
* Java语音规范保证对一个变量的读操作或者写操作是原子性(atomic,注意 i++是非原子性的,64位的long型或double型变量的读写操作也是非原子性的),但并不保证一个线程写入的值对另一个线程是可见的。
* 避免使用Thread.stop()方法,而是采用轮询(poll)机制来终止一个线程。
* 如果只需要线程间的交互通信,而不需要互斥,可以使用volatile关键字。
第67条 避免过度同步
* 为了避免活性失败和安全性失败,在一个被同步的方法或者代码块中,永远不要放弃对客户端的控制(避免在同步区域调用不可信代码,否则容易造成死锁、异常等问题)。
* 在同步代码块内做尽可能少的事情。
第68条 executor 和 task 优先于线程
* Executor service 可以等待完成一项特殊的任务再继续执行,也可以优雅的完成终止(利用awaitTerminalTermination方法),或可以在完成这些任务后逐个的获取这些任务的结果(利用ExecutorCompletionService)等。
* 对于轻载的服务器,Executors.newCachedThreadPool通常是个不错的选择;对于高负载的服务器,最好使用Executors.newFixedThreadPool
* 尽量不要编写自己的工作队列和直接使用线程;现在关键的抽象不再是Thread,而是工作单元(任务,task)。
* Timer只有一个线程来执行任务,如果唯一的线程抛出了未被捕获的异常,任务就会终止。ScheduledThreadPoolExecutor 支持多个线程,并可以优雅的从抛出未受检异常的任务中恢复。
第69条 并发工具优先于wait和notify
* 几乎没有任何理由再使用wait和notify了。
但并不是说不需要掌握,如维护旧代码可能还是需要了解的,此种情况下务必确保是利用标准的模式从while循环内部调用wait。一般情况下优先使用notifyAll而不是notify。
* 使用高级工具:
执行器框架(Executor Framework)、并发集合(Concurrent Collection)、同步器(Synchronizer,如CountDownLatch、Semaphore、CyclicBarier和Exchanger等)。
第70条 线程安全性的文档化
* 线程安全的级别:
(1)不可变的(immutable):类的实例是不可变的(如String、Long、BigInteger等)。
(2)无条件的线程安全(unconditionally thread-safe):类的实例可变,但有着足够的内部同步(如Random、ConcurrentHashMap。
(3)有条件的线程安全(conditionally thread-safe):除了有些方法为了安全的并发使用外部同步之外,与无条件的线程安全级别相同(如 Collections.synchronized 包装返回的集合,它们的迭代器要求外部同步)。
(4)非线程安全(not thread-safe):需外部同步(如ArrayList、HashMap等)。
(5)线程对立(thread hostile):这个类不能安全的被多个线程并发的使用,即使所有的方法都被外部同步包围。线程对立的根源通常在于,没有同步的修改静态数据。
* 为了避免拒绝服务攻击,应当使用一个私有锁对象(private lock object)来代替同步方法。私有锁对象只能用在无条件的线程安全类上,特别适合于专门为继承而设计的类。
第71条 慎用延迟初始化
* 正常的初始化优先于延迟初始化。
* 如果出于性能的考虑而需要对静态域使用延迟初始化,就使用 lazy initiation holder class 模式:
1 private static class FieldHolder { 2 static final FieldType field = computeFieldValue(); 3 } 4 5 static FieldType getType() { 6 return FieldHolder.field; 7 }
* 如果出于性能的考虑而需要对实例域使用延迟初始化,就使用双重校验锁:
1 // volatile 很重要,保证可见性 2 private volatile FieldType field; 3 4 FieldType getField() { 5 FieldType result = field; 6 if (result == null) { 7 synchronized(this) { 8 result = field; 9 if (result = null) { 10 field = result = computeFieldValue(); 11 } 12 } 13 } 14 return result; 15 }
第72条 不要依赖于线程调度器
* 不要让应用程序依赖线程调度器,也不要依赖 Thread.yield 或者线程优先级。这些设施仅仅对调度器作些暗示。
第73条 避免使用线程组
* 最好把线程组当作一个不成功的实验,就当它们根本不存在一样。
* 考虑使用线程池。
本文地址:https://www.cnblogs.com/laishenghao/p/9740851.html