Java并发编程实践——读书笔记(一)

Java并发编程实践——读书笔记(一)

《Java Concurrency in Practice》第一部分的阅读总结

关键字:并发、锁、线程安全、共享对象、并发容器、信号量

目录

第一章 介绍

1.1 并发的简短历史

1.2 线程的优点

​ 1.3 线程的风险

1.4 线程无处不在

第二章 线程安全

​ 2.1 什么是线程安全性

​ 2.2 原子性

​ 2.3 锁

​ 2.4 用锁来保护状态

2.5 活跃度与性能

​ 第三章 共享对象

​ 3.1 可见性

​ 3.2 发布和逸出

​ 3.3 线程封闭

​ 3.4 不可变性

3.5 安全发布

​ 第四章 组合对象

4.1 设计线程安全的类

4.2 实例限制

4.3 委托线程安全

4.4 向已有的线程安全类添加功能

4.5 同步策略的文档化

​ 第五章 构建块

​ 5.1 同步容器

​ 5.2 并发容器

​ 5.3 阻塞队列

5.4 阻塞和可中断的方法

​ 5.5 Synchronizer

5.6 为计算结果建立高效、可伸缩的高速缓存

第一章

  • 线程的风险

    • 竞争场景(race condition)

      多个线程并发地对共享数据进行操作

    • 活跃度失败(liveness failure)(程序无法继续执行或退出)

      如果安全意味着”什么坏事都没有发生“,那么活跃度关注的则是”好事最终发生了“

    • 性能危险(performance)

      ...,线程仍然会给运行时带来一定程度的开销。上下文切换(Context switches) ,...,这在多个线程组成的应用程序中是很频繁的,并且带来巨大的系统开销。...,CPU的时间会花费在对线程的调度上而不是在运行上。

第二章

  • 线程的安全性

    正确性意味着一个类与它的规约保持一致。良好的规约定义了用于强制对象状态的不变约束(invariants)以及描述操作影响的后验条件(postcoditions)

    一个类是线程安全的,是指在被多个线程访问时,类可以持续进行正确的行为

  • 原子性

    假设有操作A和B,如果从执行A的线程的角度看,当其他线程执行B的时候,耀目B全部执行完成,要么一点都没执行,这样A和B互为原子操作

    • Java.uitl.concurrent.atomic包中包括了原子变量类(atomic variable),这些类实现了数字和对象引用的原子转换(该对象的操作都是原子的)

      • AtomicLong(基本类型对应的原子对象)
      • AtomicReference(一些对象引用对应的原子对象)
    • 复合操作——”读-改-写“和”检查再运行“

      • 这是两类常见的看似原子,但是却不是原子的操作。读改写对应赋值语句,检查再运行对应条件判断
    • 内部锁

      • synchronized(锁对象的引用,锁代码块)
      • 方法级别的synchronized,锁对象是方法的调用者。静态方法的synchronized,锁对象是对应的调用者对象的class对象
      • 内部锁是一种互斥锁,至多只有一个线程可以拥有锁,但是内部锁是可重入的。
    • 重进入(Reentrancy)

      线程在试图获得它自己占优的锁的时候,如果请求成功,那么该锁是可重入的。

  • 锁对状态的保护

    对于每个可被多个线程访问的可变状态变量,如果所有访问它的线程在执行时都占有同一个锁,这种情况下,我们称这个变量是由这个锁保护的

    • 内部锁只能确保一件事,一个线程获得对象的锁之后,将阻塞其他线程获得这个锁
    • Vector 和Hashtable
      • 通过使用对象的内部锁(synchronized关键字)来封装所有的可变状态

    对于每一个涉及多个变量的不变约束,需要同一个锁保护其他所有的变量

第三章

  • 可见性(defined)

    在没有同步的情况下,编译器、处理器,运行时安排操作的执行顺序可能完全出入意料。在没有进行适当同步的多线程程序中,尝试推断那些”必然"发生在内存中的动作,你总会判断错误。

    可见性保证了,内存中的值是已定义的(也就是能够判断出来的),可能听着还是有些抽象。举个例子,多个线程对一个64位的long long变量进行值的修改,那么可能线程A正在修改变量的高32位地址,而线程B在修改变量的低32位地址,这样最后的产生的值就是undefined,也就是无法判断的。

    • 可见性比并发的要求弱, 意味着数据可能过期

    • Volatile

      • 轻量级的同步机制
      • 只保证了对应变量引用的内存是“可见的”,不保证数据同步 。
        • 从主存读,并且写入内存。(不存在cpu寄存器中,性能损失)
    • 锁不仅仅是关于同步与互斥的,也是关于内存可见的。为了保证所有线程都能够看到共享的、可变变量的最新值,读取和写入线程必须使用公共的锁进行同步

  • 对象的发布与逸出(publishing & escape)

    • 发布:使它能够被当前范围之外的代码所使用
      • 将对象的引用存储到公共静态域中
      • 从非私有方法中返回引用
      • 发布一个对象,同样也发布了该对象所有非私有域所引用的对象
    • 逸出:未经计划的发布
  • 线程封闭

    • 局部变量

    • ThreadLocal类

      ThreadLocal允许你将每个线程与持有数值的对象关联在一起。ThreadLocal提供了get和set访问器,为每个使用它的线程维护一份单独的拷贝。所以get总是返回由当前执行线程通过set设置的最新值。

      本质是全局设置一个容器,通过判断currentThread,然后存入容器中来实现。

    • 单一线程对Volatile变量进行写操作。

  • 不可变性(不可变对象)

    • 不可变对象:只有访问器方法,可变状态被封装
    • 不可变对象永远是线程安全的(可变对象的final引用,不叫不可变

第五章

  • 同步容器

    • Vector和Hashtable

    • Collections.synchronizedXxx(Xxx xxx)方法创建的同系列,如:

      List t = Collections.synchronizedList(new ArrayList()) ;
      
    • 不要轻易使用/隐藏使用同步容器的toString方法,由于被synchronized关键字修饰,串行,所以可能非常耗时。

  • 并发容器

    同步容器通过对容器的所有状态进行串行访问,从而实现了它们的线程安全。这样做的代价是削弱了并发性。并发容器就是在此基础上进行了设计,牺牲了部分的同步性来换取并发性能。

    • Queue
      • BlockingQueue
        • LinkedBlockingQueue
        • ArrayBlockingQueue
        • PriorityBlockingQueue
        • SynchronousQueue
      • ConcurrentLinkedQueue
      • Deque
        • ArrayDeque
        • LinkedBlockingDeque
    • Map
      • ConcurrentHashMap—— Hashtable/ synchronizedMap
      • ConcurrentSkipListMap ——synchronizedSortedMap
    • Set
      • ConcurrentSkipListSet —— synchronizedSortedSet
    • List
      • CopyOnWriteArrayList —— synchronizedList
        • 读不同步,写同步
  • 阻塞队列(Blocking Queue) & 生产者-消费者模式

    • 阻塞队列天然支持生产者-消费者模式
    • Deque与窃取模式
  • Synchronizer(同步器)

    • latch(闭锁)
      • 同步开始与结束
    • semaphore(信号量)
      • 实现阻塞队列
    • barrier(关卡)
      • cycle

你可能感兴趣的:(Java并发编程实践——读书笔记(一))