前言
最近发现有时候看完一本书,时间久了容易忘记,看书不总结思考效果大打折扣,故打算写这一系列文章,一是为了整理书中的要点,帮助自己消化理解;二是勉励自己多看书思考。文章中不会把书中内容讲解的非常详细,只是总结概括,适合已经阅读过该书的读者。
第一部分:基础知识
第2章:线程安全性
- Java中的主要同步机制是关键字synchronized,它提供了一种独占的加锁方式,但同步这个术语还包括volatile类型的变量,显示锁以及原子变量
原子性
- 可采用原子操作,如AutomicLong、AutomicRefference来避免竞态条件
加锁机制
每个Java对象都可以当作一个同步锁,这些锁被称为内置锁,或监视器锁
内置锁都是可重入的,意味着获取锁的操作的粒度是线程,而不是调用
第3章:对象的共享
可见性
不可见性会导致:实效数据,一个线程更新变量后,可能另一个线程不能看见
加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步
volatile声明后,编译器与运行时都会注意都这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序
加锁机制既可以确保可见性又可以确保原子性,而volatile只能确保可见性
发布与逸出
- 发布(Publish):对象能够在当前作用域之外的代码中使用
- 逸出(Escape):某个不应该发布的对象被发布时,这种情况就被称为逸出
- ps:如在构造过程中使this引用逸出的,在构造函数中启动一个线程会产生这种逸出
线程封闭
仅在单线程中访问数据,就不需要同步,这种技术称为线程封闭
维持线程封闭性规范的方法是使用ThreadLocal,这个类能使线程中的某个值与保存值的对象关联起来
不可变性
满足同步需求的另一种方法是使用不可变对象,不可变对象一定是线程安全的,需满足一下条件
- 对象创建以后其状态就不能修改
- 对象的所有域都是final类型
- 对象是正确创建的(在创建期间,this引用没有逸出)
final类型的域是不能修改的,但如果final域所引用的对象是可变的,那么这些被引用的对象是可以修改的
安全发布
- 要安全地发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确构造的对象可以通过一下方式来安全发布:
- 在静态初始化函数中初始化一个对象引用
- 将对象的引用保存在volatile类型或者AtomicReference对象中
- 将对象的引用保存到某个正确构造对象的final类型域中
- 将对象的引用保存到一个由锁保护的域中
第4章:对象组合
- 在现有的线程安全类中添加功能
- 客户端加锁机制
- 组合方式
第5章:基础构建模块
同步容器
- 包括:Vector、HashTable、Conllections.synchronizedXxx等工厂方法创建的
- 同步容器的迭代器并没有考虑到并发修改的问题,表现出的行为是及时失败(fail-fast)
- 容器的hashCode、equals、containsAll、removeAll等方法会间接执行迭代操作,并发程序中注意更改容器引起迭代器失效
并发容器
- ConcurrentHashMap替代Map
- ConcurrentSkipListMap替代SortedMap
ConcurrentSkipListSet替代SortedSet
ConcurrentHashMap
- 采用分段锁
- 返回的迭代器具有弱一致性,并非及时失败
- CopyOnWriteArrayList
- 每次修改都会创建并重新发布一个新的容器副本
- 仅当迭代操作远远多于修改操作时使用
- 阻塞队列
- LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue
同步工具
- 闭锁(Latch)
- 作用相当于一扇门,在闭锁到达状态之前,一直关着,没有线程能通过,当到达结束状态时,允许所有线程通过
- FutureTask
- 可以用做闭锁
- 信号量(Semaphore)
- 计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量
- 栅栏(Barrier)
- 所有线程必须同时到达栅栏才能继续执行
- 闭锁用于等待事件,栅栏用于等待其他线程
结构化并发应用程序
第6章:任务执行
(1)在线程中执行任务
- 串行执行:效率低
- 显示创建线程
- 线程生命周期开销非常高
- 内存等资源消耗
- 稳定性不好
(2)Executor框架
线程池:优势:重用现有的线程,在处理多个请求时分摊在线程创建和销毁过程中产生的开销;请求到达时,线程已经存在,不会延迟执行任务
- Executors.newFixedThreadPool:固定长度的线程池
- newCachedThreadPool:线程池规模不存在限制,可回收空闲线程,可按需增加新的线程
- newSingleThreadExecutor:单线程的Executor
Executor的生命周期有三个状态(1)运行(2)关闭(3)已终止
- shutdonw():执行平缓的关闭,不再接受新的任务,等待已经提交的任务执行完成
- shutdownNow():执行暴力的管理,尝试取消所有运行中的任务