参考文献

Java中线程的实现方式?

继承Thread类,重写run方法

实现Runnable接口,重写run方法

实现Callable接口,重写call方法,配合FutureTask

  • Callable一般用于有返回结果的非阻塞的执行方法
  • 同步非阻塞

线程的状态

  • NEW
  • RUNNABLE
    • READY
    • RUNNING
  • WAITING
  • TIMED_WAITING
  • BLOCKED
  • TERMINATED

并发编程的三大特性

  • 原子性
    • 问题存在的原因: 分时复用机制引起
    • 使用synchroinzedLock接口实现的锁
    • 使用CAS
  • 有序性
    • 问题存在的原因: 重排序引起
    • 使用volatile
    • 使用synchroinzedLock接口实现的锁
    • Happens-Before
  • 可见性
    • 问题存在的原因: CPU缓存引起
    • 使用volatile
    • 使用synchroinzedLock接口实现的锁
    • 使用final

Happens-Before原则

  • 程序次序规则
  • 管程锁定规则
  • volatile变量规则
  • 线程启动规则
  • 线程终止规则
  • 线程中断规则
  • 对象终结规则

Java锁的分类

  • 可重入锁,不可重入锁
    • 可重入锁:synchronized,ReentrantLock,ReentrantReadWriteLock
  • 乐观锁,悲观锁
    • 悲观锁:synchronized,ReentrantLock,ReentrantReadWriteLock
    • 乐观锁: CAS操作(Atomic原子类)
  • 公平锁,非公平锁
    • 非公平锁: synchronized
  • 互斥锁,共享锁
    • 互斥锁: synchronized,ReentrantLock
    • 共享锁: ReentrantReadWriteLock

AQS唤醒节点时为什么是从后往前唤醒

  • AQS中线程获取锁失败时,会将当前线程创建为节点(Node),并加入到同步队列(waiters)的尾部。当锁可用时,需要唤醒其中的某个节点,让其继续执行。
  • 在AQS中,唤醒节点是从尾节点向头节点依次唤醒的,即从后往前唤醒。这是因为在队列中节点的顺序是按照线程请求锁的顺序排列的,新到的线程都会加入队尾,已经等待较长时间的线程则在队头。因此,在唤醒线程时,从后往前唤醒可以保证等待时间更长的线程可以更快地得到执行,从而提高并发性能。
  • 此外,从后往前唤醒也符合CLH队列的特点。因为在CLH队列中,每个节点都保存着前一个节点的引用,按照从后往前的顺序唤醒节点可以更快地将节点从队列中移除,避免了因为节点引用无法及时释放而造成的内存泄漏和性能问题。

ConcurrentHashMap如何保证在并发情况下数据的一致性

  • ConcurrentHashMap是Java中线程安全的哈希表实现,它使用了多种技术来保证在并发情况下数据的一致性。
  • ConcurrentHashMap通过分段锁、volatile关键字和CAS操作等机制来保证数据的一致性和并发安全性。这些机制可以使ConcurrentHashMap具备较高的并发性能和可扩展性,适用于高并发场景下的数据读写操作。

分段锁机制

  • ConcurrentHashMap内部维护了多个Segment,每个Segment相当于一个小的哈希表,它们之间是相互独立的。在读写数据时,只需要锁住当前需要访问的Segment,而不是锁住整个哈希表,这样可以减小锁的粒度,提高并发性能。

volatile修饰的元素

  • 在ConcurrentHashMap的实现中,每个Segment中的元素是通过volatile修饰的,这样可以保证对于该元素的读操作与写操作都是线程安全的。因为volatile修饰的变量会保证可见性和顺序性,即每次读取该变量时都会从内存中读取最新值。

CAS操作

  • 在并发更新数据时,ConcurrentHashMap使用了CAS(Compare-And-Swap)操作,也就是乐观锁,来避免对整个Segment进行加锁。这种机制下,每个线程会先获取Segment中的元素,然后检查该元素是否被其他线程修改过。如果没有被修改,就可以直接将修改后的元素替换原来的元素;如果被修改了,就需要重新获取最新的元素并重试更新操作。

为什么调用Thread.sleep, catch异常后,调用了Thread.currentThread().interrupt();

进入 BLOCKED只有一种情况,就是等待 synchronized 监视器锁,那调用 JUC 中的 Lock.lock() 方法,如果某个线程等待这个锁,这个线程状态是什么呢?为什么?

  • WAITING

线程类的构造方法、静态块是被哪个线程调用的

  • 线程类的构造方法、静态块是被new这个线程类所在的线程所调用的,而run方法里面的代码才是被线程自身所调用的