线程

区分并行、并发

并行的英文是 parallel,同一时间平行执行。并发本质还是交替执行。

区分进程、线程、协程

区分 FutureTask、Future

Callable 接口,有返回值,只能通过 ExecutorService 执行
Future 接口,查询任务 isDone、isCancel,取消任务 cancle、阻塞等待结果 get
FutureTask 实现 Future,线程池 submit 使用 FutureTask 封装 Callable、Runnable 后执行

什么是守护线程

什么是内存模型(JMM)

线程的状态有什么联系

如何进行线程间通信(在两个线程间共享数据)

用 wait、notify,或者用阻塞队列。

如何创建、停止一个线程

Java 通过 Thread#start 创建线程,可以用 Runnable 构建,或覆盖 run()。
线程执行完 run() 就会停止,抛出异常也可以停止线程。

如何排查⼤量线程挂起的问题

// TODO

线程池

Java 使用 LWP 实现线程,上下文切换开销大,并且占用内存 1MB,到了 JDK11 才优化为 40KB。因为线程很昂贵,所以要池化线程资源,提供复用。

区分线程池 submit()、execute()

void Executor#execute(Runnable)
Future ExecutorService#submit(Callable)

Executors 提供了哪些线程池实现

scheduled DelayedWorkQueue
cached SynchronousQueue
fixed LinkedBlockingQueue
single 同 fixed

线程池的状态有什么联系

ThreadPoolExecutor 工作原理

如何调优线程池

ForkJoinPool 的工作原理

线程池,实现 work-stealing,空闲线程可以窃取任务,充分利用多处理器。它是专门为了那些可以递归划分成许多子模块设计的,目的是将所有可用的处理能力用来提升程序的性能。

同步工具

区分 synchronized、volatile

  1. 内存可见性,保证一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  2. 有序性,禁止进行指令重排序。
    synchronized 保证以上2个特性外,还保证原子性,比如i++,只能保证对单次读、写的原子性。

    如何实现 synchronized

  3. 偏向标志位1,比对线程ID,如果相同,说明当前线程占有偏向锁

  4. 如果不同,则使用 CAS 将当前线程的ID替换 Mard Word,如果成功则表示当前线程获得偏向锁
  5. 如果失败,则发生了竞争,偏向标志位改0,改为轻量级锁
  6. 当前线程使用 CAS 替换 Mark Word 为锁记录指针,如果成功,当前线程获得锁
  7. 如果失败,则发生了竞争,当前线程自旋,重试获取锁
  8. 如果自旋失败,膨胀为重量级锁,指向 monitor

    如何实现 ThreadLocal

    访问 ThreadLocal 本质是访问 Thread 的成员变量 ThradLocalMap,既然声明为成员变量、静态变量的效果一样,那么 static 还能节约内存开销。
    ThreadLocalMap 底层是 Entry[],使用开放定址法解决哈希冲突。Entry 是一个指向 ThreadLocal 的 WeakReference,使用弱引用是为了把GC主动权交给声明方,当声明方不想使用ThreadLocal,置空后就能被回收。
    Entry.value 负责保存数据,但有可能内存泄漏(ThreadLocal已GC,但value仍被Entry强引用),可以调用 remove() 及时回收。

    如何实现 Atomic

    如何实现 AQS

    CAS 存在什么问题,如何解决

    Java 同步工具类

    什么是竞态条件,举例说明

    导致程序在并发情况下出现一些bugs。多线程对一些资源的竞争的时候就会产生竞态条件,如果首先要执行的程序竞争失败排到后面执行了, 那么整个程序就会出现一些不确定的bugs。这种bugs很难发现而且会重复出现,因为线程间的随机竞争。

    区分 CountDownLatch、CyclicBarrier、Semaphore

    区分 synchronize、ReentrantLock

    synchronize 是 Java 关键字。
    ReentrantLock 可以实现 tryLock、读写锁、公平锁。

    如何实现 ReentrantReadWriteLock

    一般而言,读写锁是用来提升并发程序性能的锁分离技术的成果。Java中的ReadWriteLock是Java 5 中新增的一个接口,一个ReadWriteLock维护一对关联的锁,一个用于只读操作一个用于写。在没有写线程的情况下一个读锁可能会同时被多个读线程 持有。写锁是独占的,你可以使用JDK中的ReentrantReadWriteLock来实现这个规则,它最多支持65535个写锁和65535个读锁。

    死锁是怎么形成的

    区分活锁、死锁

    如何避免死锁