java多线程的知识点非常多,每一个都值得花费精力去细读,这里通过精炼大纲的方式提供分析的脉络
一. 多线程基础 - 常用的线程同步机制
1.1 Thead.yield()
- 暂停当前线程的执行
- 暂停时间为一个时间片
- 当前线程在一个时间片后为runnable状态
- 不释放锁
1.2 Thread.sleep(ms , nanos)
- 暂停当前线程的执行
- 暂停时间一般大于或者等于设定时间
- 当前线程在指定的时间周期内置为wait状态
- 不释放锁
1.3 object.wait
- 把当前正在执行的线程放入wait线程队列中
- 释放指定对象的锁
- 当被notify/notifyAll通知时,重新进入entry队列去
- 只能在syncrhonized代码块中调用
1.4 object.notify
- 唤醒一个在此对象wait队列中的线程
- 唤醒线程进入entry队列
- 可以任意调用
1.5 join
- 当前线程等待调用线程执行完成才继续执行
- 采用wait实现,也就是当前线程进入被join线程的waitset
- 被join线程执行结束后会notifyall唤醒当前线程.
1.6 LockSupport.park/unPark(thread)
- 精确的暂停某个线程
- unPark与park无调用先后顺序关系
- park不会释放资源
- 支持中断唤醒
- 底层实现借助posix提供的同步工具mutex和condition
二.多线程解决的问题
1.1 synchronized - 原子性 / 可见性 / 有序性
- synchronized 采用moniterEnter和moniterExit指令来解决原子性问题
- moniter是系统管程,用于控制线程并发的系统机制
- 利用对象头中的markword实现
- 包含waitset / entryset 和当前线程
- 作用于方法等同于synchronized(this)
- 作用于静态方法等同于synchronized(XXXClass.this)
- 线程解锁前,必须把共享变量的最新值刷新到主内存中;
- 线程加锁前,将清空工作内存中共享变量的值,从主内存中冲洗取值
1.2 volatile - 可见性 / 有序性
- volatile 通过内存屏障(load/store)解决有序性问题
- volatile 通过总线嗅探机制解决可见性问题
- volatile 不能解决原子性问题
三. 线程池
2.1 Executors / ThreadPoolExecutor / ExecutorService
- Executors 是一个工具类,提供四种构造ThreadPoolExecutor的方法.
- Executors.newFixedThreadPool(n)复用线程池,同时最多有n个线程同时执行
- Executors.newCachedThreadPool()复用线程池,无最多线程上限.
- ArrayBlockingQueue/work队列,采用ReentrantLock保证线程安全
- HashSet线程队列,采用ReentrantLock保证线程安全
- 几种不同的线程策略来处理线程不够时的情景
四. 工具类(JUC Java Util Concurrent)
4.1 AtomicInteger / AtomicBoolean
- 通过CAS指令来保证线程安全
- 无锁
- 使用场景 需要达到一个共同的结果,比如+到某个值或者共同达到某个条件等.
- 它不处理线程的等待与唤醒
4.2 CountdownLatch
- 底层是AQS(AbstractQueuedSynchronizer CAS+PARK)
- 减少到某个值触发线程执行,一般会有N个计算线程,一个等待线程.
- latch.await()等待条件唤醒
- latch.countDown()计算线程改变条件
4.3 CyclicBarrier
- 底层是AQS(AbstractQueuedSynchronizer CAS+PARK)
- 用法与 CountdownLatch相反, 线程处理完之后进入等待
- 等待的线程达到一定数量,则全部唤醒.
4.4 ReentrantLock
- 底层是AQS(AbstractQueuedSynchronizer CAS+PARK)
- 实现了同步队列中公平锁和非公平锁竞争机制
- 提供了等待队列
- 实现原理类似与synchronized
4.5 AbstractQueuedSynchronizer
- 是一个同步框架,在java层面通过CAS+PARK实现synchronized同步机制
- 解决线程等待,唤醒,锁重入等问题
- 使用CAS自旋竞争锁
- 使用park/unpark解决线程等待与唤醒
- 包含两个队列 同步队列CLH / ConditionObject的等待队列
4.5.1 为什么需要AQS?它解决了synchronized的什么问题?
synchronized在竞争不到资源的情况下,线程直接进入阻塞状态,无法被中断,同时也不会释放占用的资源.
AQS提供了三种机制解决中断问题:
- lockInterruptibly可以通过Thread.interupted()来中断阻塞的线程
- tryLock(long time, TimeUnit unit) 同步队列的线程, 经过一段时间没有获取到锁,会返回一个错误,可以让开发者有机会释放占用资源
- tryLock() 非阻塞性申请锁.