多线程并发实战
线程安全
Java 提供了内置锁, 来解决线程安全, 内部是一个监视器, 通过一些进入, 退出命令, 对对象实现原子性操作。 jvm 级别锁。
关于内置锁的重入的问题 实现Java 监视器模式
当一个锁锁住对象的时候 其他线程进入阻塞状态。这个线程再次发出请求的时候会重入。
共享对象
可见性:Java内存模型 主内存和工作内存
每次读取工作内存都从主内存加载最新的数据
每次修改工作内存的数据加载到主内存中。
可见性保证修改数据被其他线程可见。Vloatile 通过store load 等指令保证可见性 内置锁和Vloatile 都能保证可见性。
线程封闭
不共享数据 threadlocal 线程变量
不变对象线程安全
同步容器
早期的vector 和hashtable 内部方法加了同步锁
Collection. Synchronized 可以相应的容器变成线程安全的
Concurrent 包下的ConcurrentHashMap 分段锁读取写入可以同时进行 减少了锁的粒度。基于分段锁
CopyAndWriteArrayList 等容器 每次写入时 重新复制一份写的容器 缺点数据量大 性能比较差
阻塞队列 实现了生产消费者模式。
同步工具
协调生产者消费者的控制流,信号量 闭锁(countdownlatch 可以等待一组线程事件 futureTask 也可以做为闭锁) 栅栏
Executeor 框架
基于生产消费者模式
线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
corePoolSize- 池中所保存的线程数,包括空闲线程。需要注意的是在初创建线程池时线程不会立即启动,直到有任务提交才开始启动线程并逐渐时线程数目达到corePoolSize。若想一开始就创建所有核心线程需调用prestartAllCoreThreads方法。
maximumPoolSize-池中允许的最大线程数。需要注意的是当核心线程满且阻塞队列也满时才会判断当前线程数是否小于最大线程数,并决定是否创建新线程。
keepAliveTime - 当线程数大于核心时,多于的空闲线程最多存活时间
unit - keepAliveTime 参数的时间单位。
workQueue - 当线程数目超过核心线程数时用于保存任务的队列。主要有3种类型的BlockingQueue可供选择:无界队列,有界队列和同步移交。中可以看到,此队列仅保存实现Runnable接口的任务。
threadFactory - 执行程序创建新线程时使用的工厂。
handler - 阻塞队列已满且线程数达到最大值时所采取的饱和策略。java默认提供了4种饱和策略的实现方式:中止、抛弃、抛弃最旧的、调用者运行
无界队列
队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列做为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。最近工作中就遇到因为采用LinkedBlockingQueue作为阻塞队列,部分任务耗时80且不停有新任务进来,导致cpu和内存飙升服务器挂掉。
有界队列
常用的两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue与有界的LinkedBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。
同步移交
如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。
显示锁
reetrantLock 与lock 与内置锁不同的是 提供的无条件的 可轮询的 定时的可中断的 所以加锁和解锁都是显示的。
reetrantLock 公平和非公平两种机制
读写锁 读读不互斥 读写以及写写均互斥 使用于读多写少的程序
保证并发的实现
无锁的cas 机制
会导致aba 问题 解决 时间戳 及版本号
ASQ 机制 锁的内部实现机制
’
分布式锁的实现 redis 以及zk 临时节点 或者放在mq 的队列里。