java多线程思维精练

234 阅读4分钟

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() 非阻塞性申请锁.