一、概念
1、线程中断
2、估算线程池大小的经验公式
Ncpu = CPU的数量
Ucpu = 目标CPU的使用率,0<= Ucpu <=1;
W/C = 等待时间/计算时间
Nthreads = Ncpu*Ucpu*(1+W/C)
其中:Ncpu 可以用Runtime.getRuntime().availableProcessors();
3、线程池的拒绝策略-线程池和队列都满了的情况下
JDK自带策略:
AbortPolicy : 直接抛出系统工作异常,阻止系统正常工作
CallerRunsPolicy :
DiscardOledestPolicy:丢弃最老的请求,并尝试再提交当前任务
DiscardPolicy :默默丢弃无法处理的任务
自定义:实现RejectedExecutionHandler接口,实现拒绝策略
4、java虚拟机对锁优化所做的努力
锁偏向 : -XX:+UseBiasedLocking -可以开启偏向锁
轻量级锁 :
自旋锁 :
锁清除 : 可以使用 -XX:+DoEscapeAnalysis 打开逃逸分析
使用 -XX:+EliminateLocks 打开锁消除
5、java线程死锁
定义:死锁就是两个或者多个线程,相互占用对方需要的资源,而都不进行释放,
导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象。
分类:https://blog.csdn.net/u011116672/article/details/51051352/
6、在线程池中寻找堆栈
线程池中的 submit() 和 execute()
submit() : 会catch住线程中的Exception信息
execute(): 会打印部分的异常信息,但是不是全面
可以使用 ThreadPoolExecutor扩展线程池,让他在调度任务之前,先保存一下提交任务线程的堆栈信息(P116)
7、提高锁性能的建议
-- 减少锁持有的时间
-- 减少锁的粒度
-- 读写分离锁来替换独占锁
-- 锁分离
-- 锁粗化
二、JDK并发包
1、ReentrantLock : 的几个重要方法
lock() : 获得锁,如果锁已经被占用,则等待
lockInterruptibly() : 获得锁,但是优先响应中断
tryLock() : 尝试获得锁,如果成功,返回true,失败返回false。
该方法不等待,立即返回
tryLock(long time,TimeUnit unit):在给定的时间内尝试获得锁
unlock() : 释放锁
由于竞争的非公平性,可重入锁 可以设置公平性 例:public ReentrantLock(boolean fair);
2、Condition :提供的重要方法
await() : 使当前线程等待,同时释放当前锁,当其他线程使用
signal()或者signalAll() 方法时,线程会重新获取锁,并继续执行
awaitUninterruptibly():与上基本相同,但是它并不会在等待过程中响应中断
signal() : 唤醒一个等待中的线程
awaitNanos(long nanosTimeout): 含有定时的等待
awaitUntil(Date deadline) :
signalAll() :
3、Semaphore :信号量
1、提供的主要构造函数
public Semaphore (int permits) // 此permits 参数可以设置为0,当release()一个许可自动加一
public Semaphore (int permits, boolean fair) //第二个参数可以指定是否公平
2、主要逻辑方法
acquire() :尝试获取一个准入的许可,弱无法获得,线程会等待,
直到线程释放一个许可或者当前线程被中断。
acquireUninterruptibly():同上,但是不响应中断
tryAcquire() :尝试获取一个许可,成功返回true,失败返回false,不等待
tryAcquire(long timeout, TimeUnit unit)
release() :释放一个许可
4、ReadWirteLock : 读写锁
读写锁的访问约束情况
读 写
读 非阻塞 阻塞
写 阻塞 阻塞
☆ 只有读-读之间不阻塞
使用方式:ReetrantReadWriteLock readWriteLock = new ReetrantReadWriteLock();
Lock readLock = readWriteLock.readLock();
Lock writeLock = readWriteLock.writeLock();
5、CountDownLatch: 倒计时器
6、CyclicBarrier : 循环栅栏
-- 每满一次初始容量执行一次,可以重复使用
7、☆ LockSupport : 线程阻塞工具类
park() : 阻塞 注意:不会抛出 InterruptedException() 异常,自己处理返回。
unpark(): 释放
同suspend()和resume(); 但是不会发生死锁的现象:
原因:使用了类似信号量的机制,它为每一个线程准备了一个许可,如果许可可用,
那么park()函数会立即返回,并且消费这个许可(将许可变为不可用)
如果许可不可用,就会阻塞。而unpark()则使得一个许可变为可用(与信号量不同的是,
许可不能累加,你不可能拥有超过一个许可,它永远只有一个)
8、JDK 提供了 Executor 框架
9、线程池的拒绝策略-线程池和队列都满了的情况下
JDK自带策略:
AbortPolicy : 直接抛出系统工作异常,阻止系统正常工作
CallerRunsPolicy :
DiscardOledestPolicy:丢弃最老的请求,并尝试再提交当前任务
DiscardPolicy :默默丢弃无法处理的任务
自定义:实现RejectedExecutionHandler接口,实现拒绝策略
10、ThreadFactory :自定义线程创建
-- 是一个接口 只有一个方法创建线程
11、ThreadPoolExecutor : 可扩展线程池
重要方法-- beforeExecute() :
-- afterExecute() :
-- terminated() :
最重的构造函数:
public ThreadPoolExecutor(
int corePoolSize, // 指定了线程池中的线程数量
int maximumPoolSize, // 指定了线程池中的最大线程数量
long keepAliveTime, // 当线程池线程数量超过 corePoolSize时,多余的空闲线程的存活时间
TimeUnit unit, // 上一个变量的单位
BlockingQueue<Runnable> workQueue, // 任务队列,被提交但是尚未被执行的任务
ThreadFactory threadFactory, // 线程工厂,用于创建线程,一般用默认的即可
RejectedExecutionHandler handler // 拒绝策略,当任务太多来不及处理,如何拒绝任务
)
12、ExecutorService:
invokeAll(tasks) : 等待所有任务完成后统一返回
invokeAll(tasks,timeout,unit) : 同上+等待时间
invokeAny(): : 有一个任务完成后返回,会调用interrupt()方法中断其他任务
☆: invokeAny 与 执行慢的任务异常
执行慢的任务出现异常时,默认情况下不会在控制台输出异常信息。
如果显式使用try…catch语句块则可以自定义捕获异常。
加入显式的try-catch语句块可以捕获异常信息,
但抛出的异常在main方法中没有捕获到,
说明子线程出现异常是不影响main线程的主线程的
☆: invokeAny()与执行快的任务异常。
先出现异常而不影响后面任务的取值的原理是在源代码中一直判断有没有正确的返回值,
如果直到最后都没有获得返回值则抛出异常,这个异常是最后出现的异常。
比如A,B,C三个任务一起执行,都出现了异常,则最终的异常就是最后出现的异常。
☆: invokeAny()出现异常返回最后一个异常并输出
13、Fork/Join框架:分而治之
ForkJoinPool接口 方法:public<T> ForkJoinTask<T> submit(ForkJoinTask<T> task)
ForkJoinTask: 有两个子类 RecursiveAction(没有返回值) 和 RecursiveTask (可以携带返回值)
三、JDK的并发容器
1、ConcurrentHashMap:
2、CopyOnWriteArrayList:
3、ConcurrentLinkedQueue: 高效的并发队列,使用链表实现,可以看做一个线程安全的LinkedList
单向链表
offer():添加元素
poll() :弹出元素
4、BlockingQueue: 接口:JDK通过链表、数组等方式实现了这个接口,表示阻塞队列,非常适合用于作为数据共享的通道
实现:ArrayBlocking
5、ConcurrentSkipListMap: 跳表
四、锁的优化及注意事项
1、AtomicReference : 无锁的对象引用
-- 但是没有版本信息,两次更新一致则没有区别
2、AtomicStampedReference:带有时间戳的对象引用
-- 时间戳(版本号)
3、AtomicIntegerFieldUpdater : 使普通变量享受原子操作
-- Updater有三种:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
分别对int、long、普通对象进行CAS修改
注意事项:(1)Updater 只能修改它可见范围内的变量,因为Updater使用反射得到这个变量。如果变量不可见就会出错
(2)变量类型必须是volatile类型
(3)由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此不支持static关键字
4、SynchronousQueue :容量为0,
-- 任何一个对 SynchronousQueue 的写需要等待一个对 SynchronousQueue 的读,反之亦然。因此更像是一个数据交换通道
5、ConcurrentLinkedQueue : 使用无锁的CAS操作:性能高
6、Disruptor :无锁的缓存框架
8、 NIO SelectionKey中定义的4种事件
SelectionKey.OP_ACCEPT —— 接收连接继续事件,表示服务器监听到了客户连接,服务器可以接收这个连接了
SelectionKey.OP_CONNECT —— 连接就绪事件,表示客户与服务器的连接已经建立成功
SelectionKey.OP_READ —— 读就绪事件,表示通道中已经有了可读的数据,可以执行读操作了(通道目前有数据,可以进行读操作了)
SelectionKey.OP_WRITE —— 写就绪事件,表示已经可以向通道写数据了(通道目前可以用于写操作)
9、有界线程池/资源池 与相互依赖的任务不能一起使用