读《Java并发编程实践》

    技术2022-05-19  22

    读《Java并发编程实践》

    随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。

    多处理器与多核处理器的区别:多核是指一个处理器里面有多个处理核心,而多处理器就是在一台机器上有多个处理器。很多商家在卖机的时候仍将一个处理核心说成是一个处理器,将多核说成多处理器,一般单机上多数人说的多处理器就是多核的意思 。如下图示,

    多处理器系统

    多核处理器

    过去的三十年间,计算机性能一直由摩尔定律来推动;从今天起,它将由Amdahl阿姆德尔)定律推动。

    Java 5.0 是在Java开发并发应用程序的进程中,迈出的巨大一步。它提供了新的高层组件以及更多的低层机制,这些将使得一名新手更容易像专家那样去构建并发应用程序。

    线程是控制和利用多处理器系统计算能力的最简单的方式。同时,伴随着处理器数量的增加,有效地采用并发会变得越来越重要。

    因为程序调度的基本单元是线程,一个单线程应用程序一次只能运行在一个处理器上。使用多线程可以帮助我们在单处理器系统中实现更佳的吞吐量。例如,当第一个线程等待I/O而阻塞时,另一个线程也可以运行,这样就可以提高CPU的利用效率。

    下面是读《Java并发编程实践》后的总结

    曾经对Java并发编程不屑一顾,曾经也听到过别人对Java多线程不屑一顾,而今真正看了《Java并发编程实践》这本书才让我懂得以前的我是多么的渺小,犹如沧海一粟;又如井底之蛙,坐井观天。我们知道,使用多线程最主要的原因就是提高性能,然而伴随着性能的提高,程序的安全性浮现在了我们的面前。故,如何提高性能的同时保证程序的安全性是我们研究核心所在。再次提醒,安全性永远排在第一位,它比提高性能更重要!

    第一章,简介。简短地描述多线程产生、发展的因素;使用线程的一些优点;伴随着线程带来的安全风险;最后,我们须知:线程无处不在。

    第二章,线程安全。什么样的类是线程安全的?原子性的概念;锁的介绍;如何用锁来保护状态;活跃度及性能的引入。

    1, 保证线程安全的三种方法 :

    a, 不要跨线程访问共享变量;

    b, 使共享变量是 final类型的;

    c, 将共享变量的操作加上同步。

    2, 一开始就将类设计成线程安全的,比在后期重新修复它,更容易。

    3, 编写多线程程序, 首先保证它是正确的,其次再考虑性能。

    4, 无状态或只读对象永远是线程安全的。

    5, 不要将一个共享变量裸露在多线程环境下(无同步或不可变性保护)

    6, 多线程环境下的延迟加载需要同步的保护, 因为延迟加载会造成对象重复实例化

    7, 对于 volatile 声明的数值类型变量进行运算, 往往是不安全的 (volatile 只能保证可见性, 不能保证原子性)

    8, 当一个线程请求获得它自己占有的锁时 (同一把锁的嵌套使用)我们称该锁为可重入锁在 jdk1.5 并发包中, 提供了可重入锁的 java 实现 -ReentrantLock.

    9, 每个共享变量都应该由一个唯一确定的锁保护创建与变量相同数目的 ReentrantLock, 使他们负责每个变量的线程安全

    10,虽然缩小同步块的范围可以提升系统性能但在保证原子性的情况下不可将原子操作分解成多个synchronized块

    第三章共享对象。什么是可见性?发布和逸出。

    11, 在没有同步的情况下编译器与处理器运行时的指令执行顺序可能完全出乎意料原因是编译器或处理器为了优化自身执行效率而对指令进行了的重排序 (reordering)

    12, 当一个线程在没有同步的情况下读取变量, 它可能会得到一个过期值, 但是至少它可以看到那个线程在当时设定的一个真实数值而不是凭空而来的值这种安全保证称之为最低限的安全性 (out-of-thin-air safety)

    13, volatile 变量 只能保证可见性无法保证原子性

    14, 某些耗时较长的网络操作或IO确保执行时不要占有锁

    15, 发布 (publish) 对象指的是使它能够被当前范围之外的代码所使用(引用传递)

    对象逸出 (escape), 指的是一个对象在尚未准备好时将它发布 

    原则为防止逸出对象必须要被完全构造完后才可以被发布 ( 最好的解决方式是采用同步 )

    this 关键字引用对象逸出

    例子在构造函数中开启线程并将自身对象 this 传入线程造成引用传递而此时构造函数尚未执行完就会发生对象逸出了所以,应避免在构造函数中启动线程!

    16, 必要时使用 ThreadLocal变量确保线程封闭性 (封闭线程往往是比较安全的但一定程度上会造成性能损耗 )

    封闭对象的例子在实际使用过程中比较常见例如 hibernate openSessionInView机制jdbc的 connection机制 .

    17, 单一不可变对象往往是线程安全的 (复杂不可变对象需要保证其内部成员变量也是不可变的 )

    良好的多线程编程习惯是将所有的域都声明为 final除非它们是可变的 

    18, 保证共享变量的发布是安全的 

    a, 通过静态初始化器初始化对象 (jvm 会保证静态初始化变量是同步的 )

    b, 将对象申明为 volatile 或使用 AtomicReference;

    c, 保证对象是不可变的

    d, 将引用或可变操作都由锁来保护

    19, 设计线程安全的类应该包括的基本要素

    a, 确定哪些是可变共享变量

    b, 确定哪些是不可变的变量

    c, 指定一个管理并发访问对象状态的策略

    20, 将数据封装在对象内部并保证对数据的访问是原子的建议采用 volatile javabean 模型或者构造同步的 getter,setter.

    21, 线程限制性使构造线程安全的类变得更容易因为类的状态被限制后分析它的线程安全性时就不必检查完整的程序

    22, 编写并发程序需要更全的注释更完整的文档说明

    23, 在需要细分锁的分配时使用 java监视器模式好于使用自身对象的监视器锁前者的灵活性更好

    Object target = new Object();

    // 这里使用外部对象来作为监视器, 而非 this

    synchronized(target) {

    // TODO

    }

    第四章,组合对象。

    24, 设计并发程序时 在保证伸缩性与性能折中的前提下优先考虑将共享变量委托给线程安全的类由它来控制全局的并发访问

    第五章,构建块。同步容器的介绍;并发容器的添加;以及一些Synchronizer的概念,比如Latch、FutureTask、Semaphore及Barriers等。

    25, 使用普通同步容器 (Vector, Hashtable) 的迭代器需要外部锁来保证其原子性 原因是 普通同步容器产生的迭代器是非线程安全的

    26, 在并发编程中需要容器支持的时候优先考虑使用 jdk 并发容器 (ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList…)

    27, ConcurrentHashMap, CopyOnWriteArrayList并发容器的迭代器以及全范围的 size(), isEmpty() 都表现出弱一致性他们只能标示容器当时的一个数据状态 无法完整响应容器之后的变化和修改

    28, 使用有界队列在队列充满或为空时阻塞所有的读与写操作 ( 实现生产 – 消费的良好方案 )BlockQueue 下的实现有 LinkedBlockingQueue 与 ArrayBlockingQueue, 前者为链表可变操作频繁优先考虑后者为数组读取操作频繁优先考虑

    PriorityBlockingQueue 是一个按优先级顺序排列的阻塞队列它可以对所有置入的元素进行排序 ( 实现 Comparator 接口 )

    29, 当一个方法能抛出 InterruptedException则意味着这个方法是一个可阻塞的方法如果它被中断将提前结束阻塞状态当你调用一个阻塞方法也就意味着本身也称为了一个阻塞方法因为你必须等待阻塞方法返回如果阻塞方法抛出了中断异常我们需要做的是将其往上层抛除非当前已经是需要捕获异常的层次如果当前方法不能抛出 InterruptedException可以使用 Thread.currentThread.interrupt() 方法手动进行中断

     

    第六章,任务执行。如何在线程中执行任务?Executor框架的介绍;Callable及Future的认识。

    第七章,取消和关闭。

    第八章,应用线程池。

    第九章,GUI应用程序。

    第十章,避免活跃度危险。介绍了死锁的概念;如何诊断和避免死锁;其它的活跃度危险:饥饿、弱响应性及活锁。

    第十一章,性能和可伸缩性。对性能的全方位剖析,Amdahl定律的引入;线程引入的开销;如何减少锁的竞争。

    第十二章,测试并发程序。

    第十三章,显示锁。Lock和ReentrantLock;对性能的考量;公平性;在synchronized和ReentrantLock之间进行抉择;读写锁。

    第十四章,构建自定义的同步工具。

    第十五章,原子变量与非阻塞同步机制。锁的劣势;CAS的概念;原子变量类Atomic;非阻塞算法。

    第十六章,Java存储模型。什么是存储模型,要它何用?如何安全发布。

    实话实说,已经看了两遍《java并发编程实践》了,但差不多现在只掌握了30%的内容。革命尚未成功,同志仍需努力!继续感悟。。。


    最新回复(0)