死锁
- 两个线程持有的锁互相嵌套
- 主线程向单线程池提交任务A,任务A内部也向该线程池提交任务B
Thread的start()和run()区别
6种线程状态
AQS
lock():
tryAcquire() => addWaiter() => enq() => LockSupport.park() => Unsafe.park()
加入到AQS链表尾部
unlock():
从尾部向前遍历,找到head的下一个Node,调用LockSupport.unpark(node.thread)
Condition.await()/signal()
两个AQS队列
CountDownLatch
Semaphore
Semaphore semaphore=new Semaphore(5); //设置AQS的state
semaphore.acquire(); //AQS的state-1
semaphore.release(); //AQS的state+1
state = 0时就阻塞
CyclicBarrier
内部基于ReentrantLock和Condition实现,这次没有借助AQS的state,而是CyclicBarrier维护了成员变量count
单线程池
利用队列FIFO的特性,异步顺序处理Task
线程池
如果当前线程数小于corePoolSize,会调用addWork()方法创建一个线程并调用start(),后续任务执行时只是简单调用其run()方法
线程池线程数
CPU密集型:多让CPU运行,减少上线文切换带来的中断。核数 + 1
IO密集型:CPU多空闲,允许多中断,核数 * 2
AQS
一个线程只能存在同步队列(双向链表)或条件队列(单向链表)中的一个,如果被condition.await()了,则进入条件队列,而且如果没有线程signal()唤醒,则永远等待;而同步队列只需要上一个线程释放了锁,就会进入Runnable状态。
AQS有两种模式:共享锁和排它锁
ReentrantLock就是排它锁;ReadWriteLock、CountDownLatch、Semaphore都是共享锁(AQS的state参数作计数器)。
CyclicBarrier直接使用ReentrantLock和Condition就实现了,计数器在CyclicBarrier的成员变量位置。
ABC线程依次执行
可以用Condition,join()实现。
优雅关闭线程
Runtime.getRuntime().addShutdownHook();
jvm关闭前就需要先并发执行所有ShutdownHook,如果直接kill -9的话就比较粗暴了。
工作中对多线程的使用
并发:查询税盘在线状态。使用CountDownLatch.await(1000, TimeUnit.SECONDS);
异步:抽取发票落库,查开票点时异步查询CA状态。
SynchronousQueue
不存储,就是个配对器
Executors类:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
线程池监控
有些场景下不知道如何配置线程池,或者担心配置的线程池过大或过小。
自定义方法,定时监控活跃线程数,等待队列数等指标