【读书笔记】《Effective Java》(9)--并发

来源:互联网 发布:海口数据共享交换平台 编辑:程序博客网 时间:2024/05/01 01:56

这一部分应该是Java很难的一部分,不过《Effective Java》讲到的不算多,建议先补一下Java多线程的知识,比如,我这几天找到Java并发编程这里,恶补了下多线程的知识。

并发

66. 同步访问共享的可变数据

  • 同步不仅是互斥地访问对象,还意味着其他线程对于对象的操作(线程间可靠的通信),己方可见

  • Java语言规范保证读或者写一个变量是原子的,除非这个变量的类型为long或double(这是书上的原话,不过我查了一下,改为给一个基本变量读取或者赋值是原子的更好,至于long和double,我想是因为它们存储空间占两个寻址空间,读取时需要两次寻址,高32位低32位这样,所以不能保证原子性)

  • 活性失败:指因为JVM的优化,导致一段代码不能及时有效地被执行而并发失败的现象,这并不会产生任何异常,但会因为临界条件无法达到,一部分代码一直不能得到执行

  • 安全失败:指因为代码没有良好的同步,导致预期目标没有实现而并发失败的现象,这并不会产生任何异常,但是得到的结果并不是我们想要的

67. 避免过度同步

  • 本条建议:

    1. 开放调用:即将外来代码的调用放到同步区域之外,同步区域只执行尽可能少的工作
    2. 如果一个类不是为了并发而设计的,那么不要使用内部同步(内部类的设计的时候使用同步的方法),而是让客户自己在必要时刻进行外部同步。
  • Java平台没有遵守建议的类:StringBuffer,它是内部同步的,但常常用于单线程


68. executor和task优先于线程

  • 现在应当优先使用executor和task而不是Thread或者Runnable,因为在以前Thread既是工作单元又是执行单元,但是现在二者分离,executor是执行单元,Runnable和Callable是工作单元

  • 使用ScheduledThreadPool代替Timer作为多线程的定时器

  • 对于ThreadPool:

    1. 使用Executor.newCachedThreadPool来应对一个轻负载的服务器
    2. 使用Executor.newFixedThreadPool来应对一个重负载的服务器
    3. 为了最大限度控制,直接使用ThreadPoolExecutor

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

  • Java并发包包含Executor Framework、并发集合以及同步器三大部分

  • 使用同步器而不是wait和notify

  • 使用wait和notify的建议:

    1. 使用wait的正确方式:在循环中调用wait
    synchronized(obj){    while(<condition does not hold>){        obj.wait();    }}
    1. 使用notifyAll而不是notify避免恶意访问

70. 线程安全性的文档化

  • 线程安全的级别:

    1. 不可变:类的实例是不可变的,也就是无需同步,也不会出并发问题
    2. 无条件的线程安全:类的实例是可变的,但是实现了足够的内部同步,可以并发,例如并发包内的集合类
    3. 有条件的线程安全:除了有些方法为了进行安全的并发而使用外部同步之外,线程安全级别和无条件的线程同步相同,例如Collections.synchronized返回的类,其iterator(迭代器)需要外部同步
    4. 非线程安全:实例可变,为了并发使用,需要客户代码对其进行外部同步每个方法,例如一般的集合类
    5. 线程对立:不能安全地被多个线程并发,根源通常在于,没有同步地修改静态数据
  • 本条建议:

    1. 如果编写无条件的线程安全类,建议使用私有锁,隐藏内部实现,私有锁尤其适合于专门为继承而设计的类
    2. 如果编写有条件的线程安全类,一定要在API文档中注明哪个方法调用序列需要外部同步,以及执行这些序列需要哪把锁

71. 慎用延迟初始化

  • 四种延迟初始化方式:

    1. 最普通的:
    private FieldType field;synchronized Field getField(){    if(field==null){        field=computeFieldValue();    }    return field;}
    1. 对静态域的出于性能考虑的初始化方式(lazy initialization holder class):
    private static class FieldHolder{    static final FieldType field=computeFieldValue();}static FieldType getField(){return FieldHolder.field;}
    1. 双重检查模式(出于性能考虑,避免初始化之后的锁定访问的开销):
    private volatile FieldType field;Field getField(){    FieldType result=field;    if(result==null){        synchronized(this){            result=field;            if(result==null){                field=result=computeFieldValue();            }        }    }    return result;}
    1. 单重检查模式(双重检查模式的变式,可以接受重复初始化):

    “`java
    private volatile FieldType field;
    private FieldType getField(){
    FieldType result=field;
    if(result==null){
    field=result=computeFieldValue();
    }
    return result;
    }

“`

72. 不要依赖于线程调度器

  • 本条建议:
    1. 线程只做有意义的工作,不应该忙等
    2. 不要依赖Thread.yield改变优先级,因为它在不同环境下效果不同,而且实现也不同
    3. 不要使用调整线程优先级的方法,它不具有可移植性
    4. 线程优先级可以提高本来就可以正常工作的程序的服务质量,而不能修正一个本来不能工作的程序

73. 避免使用线程组

  • 线程组已经是一个废弃的类,在Java1.5之后不应当使用

0 0
原创粉丝点击