EffectiveJava——并发

第66条:同步访问共享的可变数据

synchronized:互斥性+可见性
volatile:可见性

Java语言规范保证读写一个变量是原子的(atomic),除非这个变量是long或者double。
注意++或者–等操作符表示原子的。两个线程操作一个共享数据,如果第二个线程在第一个线程的读取旧数据与写入新值期间读取了这个值,第二个线程就和第一个线程看到了同一个值,并返回了相同的序列号。这就是安全性失败。

第67条:避免过度同步

通常应该在同步代码块中做尽可能少的工作。

锁是可重入的。
对于有并发修改的需求,考虑使用CopyOnWriteArrrayList.

总结:为了避免死锁和数据破坏,千万不要从同步区域内部调用外来方法,尽可能的控制同步代码块中内部的工作量。

第68条:executor和task优先于线程

不要单独的使用newThread,也不要自己编写队列。
考虑使用Executors或ThreadPoolExecutor

第69条:并发工具优先于wait和notify

ConcurrentMap
BlockQueue
CountDownLatch:倒计数锁存器,latch:门闩; 弹簧锁
Semaphore:信号
CyclicBarrier:循环障碍
Exchanger:交换器
System.nanoTiem:间歇性的用时计算

始终应该使用wait循环模式来调用wait方法,永远不要在循环之外调用wait方法

synchronized(obj){
   while(<condition dose not hold>){
      obj.wait();
   }
}

总之:直接使用wait和notify就像使用“并发汇编语言”进行编程一样,而java.util.concurrent则提供了更高级的语言,没有理由在新代码中使用wait和notify,即使有也是少数,如果使用wait和notify,必须始终使用while循环内部调用wait的标准模式,一般情况下,应该有效考虑使用notifyAll,而不是使用notify。

第70条:线程安全性的文档化

线程安全的几种级别:
- 不可变性:String,Loing,BigDecimal
- 无条件的线程安全,实例可变,但是内部有着足够的同步:Random,ConcurrentHashMap
- 有条件的线程安全:部分方法线程安全,Collections.synchronized包返回的集合,他们的迭代器(iterable)要求外部同步
- 非线程安全
- 线程对立:这个对象不能安全的被多个线程并发使用,即使是在同步的情况下

总之:每个类都应该有线程安全注释,如果编写的是无条件线程安全类,就应该考虑使用私有对象锁,防止客户端程序的子类化不同步干扰。

第71条:慎用延迟初始化

大多数的情况下,正常的初始化要优先于延迟初始化。

1:如果处于性能的考虑而需要对静态域使用延迟初始化,就使用lazy initalization holder class模式:

//延迟加载
private static class FieldHolder{
   static final FieldType field = computeFieldValue();
}

static FidldType getFidld(){
   return FieldHolder.field;
}

但getField方法第一次被调用时,它第一次读取FieldHolder.field,导致FieldHolder类得到初始化。

2:如果处于性能的考虑而需要对实例域使用延迟初始化,就使用双重检查模式:

第72条:不要依赖于线程调度器

任何依赖于线程调度器达到正确性或者性能要求的程序,都可能是不可移植的。

要可移植,响应良好,健壮的应用程序,最好的做法就是:确保可运行的线程数量不明显多余处理器的数量

如果线程没有在做有意义的工作,就不应该运行。
线程不应该一直处于忙-等的状态。
不要企图通过Threa.yield来修正该程序。
线程优先级是Java平台最不可移植的特种

对于线程不应该一直处于忙-等的状态的理解:

//应该禁止使用这种形式的wait
public void wait(){
   while(true){
      synchronized(this){
         if(count == 0)return;
      }
   }
}

第73条:避免使用线程组

线程组唯一的功能是允许你把thread的某些基本功能应用到一组线程上。

总是不要使用线程组。

由于计划接下来对并发相关技术进行深入学习,所以这里只做简单记录

你可能感兴趣的:(EffectiveJava——并发)