线程池 Doug Lea
- 普通线程池
- 定时线程池
ThreadPoolExecutor
构造方法参数:
corePoolSize :池中所保存的线程数,包括空闲线程
• maximumPoolSize:池中允许的最大线程数
• keepAliveTime: 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间
• unit:keepAliveTime 参数的时间单位
• workQueue :执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务
• threadFactory:执行程序创建新线程时使用的工厂
• handler :由于超出线程范围和队列容量而使执行
submit方法:
- 小于核心
addWork - 大于核心
- 直接增加任务
- 如果3增加失败 就拒绝
拒绝策略
–
线程池有四种拒绝策略:
AbortPolicy:抛出异常,默认
CallerRunsPolicy:不使用线程池执行
DiscardPolicy:直接丢弃任务
DiscardOldestPolicy:丢弃队列中最旧的任务 对 于 线 程 池 选 择 的 拒 绝 策 略 可 以 通 过 RejectedExecutionHandler handler = new
ThreadPoolExecutor.CallerRunsPolicy();来设置。
Jps>jstack
jstack 是排查线程死锁
Submit&futuretask方法区别
- 有无返回值
- Task不一样 futuretask一个task本身
CAS中有乐观锁思想及实现
多线程中的三个关键字
- 原子性
- 可见性
- 有序性
java层面关键字解决上述三个问题:
– - volatile: 保证可见性、有序性
⚫保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
⚫ 禁止进行指令重排序。 - synchronized:重量级锁、重(chong)入锁
⚫ 对于普通同步方法,锁是当前实例对象。
⚫ 对于静态同步方法,锁是当前类的 Class 对象。
⚫ 对于同步方法块,锁是 Synchonized 括号里配置的对象。
方法中:ACC_SYNCHRONIZED、代码块:monitorenter monitorexit
lock锁: - ReentrantLock 重入锁: 手动,lock指令,比synchornized更加灵活
Condition适用于生产者消费者模型 - ReentrantReadWriteLock 读写锁
jdk1.8之后Stamped Lock
try{
//上锁
lock.lock();
...
}finally{
//解锁
lock.unlock();
}
方法
• void lock() 获取锁,调用该方法当前线程将会获取锁,当锁获取后,该方法将返回。
• void lockInterruptibly() throws InterruptedException 可中断获取锁,与 lock()方法不同之处在于该方
法会响应中断,即在锁的获取过程中可以中断当前线程
• boolean tryLock() 尝试非阻塞的获取锁,调用该方法立即返回,true 表示获取到锁
• boolean tryLock(long time,TimeUnit unit) throws InterruptedException 超时获取锁,以下情况会返回:
时间内获取到了锁,时间内被中断,时间到了没有获取到锁。
• void unlock() 释放锁
统计:
java.util.concurrent.locks.ReentrantLock#getHoldCount
java.util.concurrent.locks.ReentrantLock#getQueuedThreads
- ReentrantReadWriteLock 读写锁
细粒度问题 ,读是共享的、写是独占的
java8 增加了对读写锁的优化:StampedLock - AbstractQueuedSynchronizer 队列同步器
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire 独占式获取同步状态
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireInterruptibly 独占式获取同步状态,未获取可以
中断
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireShared 共享式获取同步状态
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireSharedInterruptibly 共享式获取同步状态,未获
取可以中断
java.util.concurrent.locks.AbstractQueuedSynchronizer#release 独占释放锁
java.util.concurrent.locks.AbstractQueuedSynchronizer#releaseShared 共享式释放锁
- CountDownLatch (同步工具类)
CountDownLatch 时,需要指定一个整数值,此值是线程将要等待的操作数。当某个线程为了要执行这些操作而等待时,需要调用 await 方法。await 方法让线程进入休眠状态直到所有等待的操作完成为止。当等待
的某个操作执行完成,它使用 countDown 方法来减少 CountDownLatch 类的内部计数器。当内部计数器递减为 0 时,CountDownLatch 会唤醒所有调用 await 方法而休眠的线程们。
⚫ java.util.concurrent.CountDownLatch#await()
⚫ java.util.concurrent.CountDownLatch#countDown()
⚫ java.util.concurrent.CountDownLatch#getCount()
原理
CountDownLatch 的构造函数接收一个 int 类型的参数作为计数器,如果你想等待 N 个点完成,这里就传入 N。
当我们调用 CountDownLatch 的 countDown 方法时,N 就会减 1,CountDownLatch 的 await 方法会阻塞当前线程,直到 N 变成零。由于 countDown 方法可以用在任何地方,所以这里说的 N 个点,可以是 N 个线程,也可以是 1 个线程里的 N 个执行步骤。用在多个线程时,只需要把这个CountDownLatch 的引用传递到线程里即可。CountDownLatch 是一次性的,只能通过构造方法设置初始计数量,计数完了无法进行复位,不能达到复用。
- Semaphore (信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
java.util.concurrent.Semaphore#acquire() 获取许可
java.util.concurrent.Semaphore#release() 释放许可
java.util.concurrent.Semaphore#tryAcquire() 尝试获取许可
雪花算法
HashMap
如何优化扩容?
估算大小,初始设定
ReentrantLock