多线程:

多线程要注意哪些问题:

原子性:提供互斥访问

可见性:某个线程对主内存的修改可以及时被其他线程看到

有序性:由于指令重排

线程:

创建线程的方式,怎样启动,怎样停止

创建:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口与FutureTask(有点不好用)
  4. 使用线程池

启动:

Thread类的start()方法。

如何停止:

sleep()

interrupt()

线程的状态有哪些,blocked和waiting有什么区别:

状态有哪些:

img

blocked和waiting有什么区别:

img

主要是触发条件和唤醒条件。

虽然最终表现都是“线程挂起”,但 挂起的理由不同、唤醒的源头不同、重新运行前还要不要抢锁也不同

sleep和wait的区别,sleep会释放cpu吗:

区别:

img

sleep()方法让出cpu进入阻塞队列

wait()方法释放掉当前同步块的锁并且让出cpu进入阻塞队列

notify和notifyAll的区别,notify选哪个线程:

都是object的方法

notify选择唤醒的一个线程是任意的,具体实现依赖JVM。

notifyAll唤醒等待锁的所有线程

LockSupport.park()和LockSupport.unpart():

用本地方法来实现的等待与唤醒操作主要在AQS框架下使用

不同线程之间如何通信:

共享变量是最基本的线程间通信方式。

1:Object类
synchronized+wait()+notify()/notifyAll()

2:显示锁+条件变量

3:volatile关键字

并发安全:

等待资源的状态(blocking状态)可以分为2种:

运行态的轮询,会消耗cpu

挂起到阻塞态,有线程切换也会消耗cpu,还会用到系统的mutex锁

synchronized和AQS都是实现类似管程的机制,他们的结构也可以一一对应上

锁的类别:

如何理解可重入锁:

img

如何理解乐观锁和悲观锁:

img

如何理解公平锁和非公平锁:

公平锁:多个线程按照顺序来获取锁,线程直接加入队列中排队,只有队列中第一个线程才能获得锁。

非公平锁:多个线程枷锁时直接尝试获得锁,能抢到锁直接占有锁,抢不到才到等待队列中等待。

CAS:

什么是CAS:

一种乐观锁机制,包含三个操作数(内存位置,预期值,新值),如果内存V上的值等于预期值,就将其更新为新值,整个过程是原子性的,通过硬件支持来实现的。

CAS和AQS的关系:

CAS为AQS提供原子操作

CAS的缺点:

ABA问题

轮询消耗大

只能保证一个共享变量操作的原子性

为什么不能所有锁都用CAS:

高并发情况下,大量轮询会消耗系统资源

JAVA如何解决CAS的ABA问题的:

通过版本号的方法

synchronized:

实现原理:

在编译后会在同步代码块前后加上monitorenter和monitorexit。

维护waitSet和entryList:

  1. entryList
    用来存放 因为抢锁失败而被阻塞 的线程(BLOCKED)。
    新线程或重入失败的线程都会被放到这里,等待锁被释放后由 JVM 挑选一个唤醒。
  2. waitSet
    只存放 已经持有锁,但在该锁上调用了 obj.wait() 的线程(WAITING/TIMED_WAITING)。
    这些线程必须先拿到过锁,主动放弃锁并等待 notify/notifyAll 才会被放进去。

讲解下锁升级:

无锁->偏向锁->轻量级锁->重量级锁

  1. 无锁(001)
    刚 new 出来的对象、计算过 hashCode 或偏向延迟未过前都处此状态。
    不等于“没加锁”,只是尚未偏向任何线程
  2. 偏向锁(101)
    • 启动后 4 s(-XX:BiasedLockingStartupDelay)内不会启用,所以“无锁→偏向锁”有延迟
    • 第一次加锁时把当前线程 ID 写入 mark word,之后该线程进出同步块不做任何 CAS,性能最高。
    • 只要出现任何竞争(即使是同一线程重入以外的线程尝试查看锁),偏向立即撤销。
  3. 轻量级锁(00)
    • 撤销偏向后或第二个线程尝试加锁时进入。
    • 原持有线程先把 mark word 拷贝到栈帧锁记录(Displaced Mark Word),后到的线程自己栈帧里自旋 CAS。
    • 自旋次数-XX:PreBlockSpin 或自适应算法决定;自旋失败才膨胀。
  4. 重量级锁(10)
    • 自旋失败、或锁被重入次数过多、或等待线程数 > CPU 核心数时膨胀为 monitor。
    • 此时线程被操作系统挂起/唤醒(pthread_cond_wait/futex),开销最大。

synchronized支持可重入吗:

支持。

synchronized是公平锁吗:

不是。

被唤醒的线程要跟还没进入队列的线程一起抢

ThreadLocal:

作用:

线程隔离:为每个线程提供了独立的变量副本,线程之间不会相互影响

降低耦合度:在同一个线程内的多个函数或组件之间,使用ThreadLocal可以减少参数的传递,降低代码之间的耦合度

原理:

依赖于Thread中的ThreadLocalMap字段

有set(),get(),remove()方法

可能存在的问题:
ThreadLocalMap中key(也就是ThreadLocal)是弱引用,value是强引用,如果不清除,key就睡随着GC销毁而value就会留在那里造成内存泄漏。可以通过用remove()方法来解决。

key 消失后取不到值的问题怎么解决?

因为 static final 把 ThreadLocal 实例提升为“类级别”的强引用
只要该类所在的 ClassLoader 未被卸载,这个引用就永远不会被 GC

voliatle:

作用:

保证变量对所有线程的可见性:被修饰的变量每次写都会立刻刷新回主存,每次读都从主存中读取。

禁止指令重排序优化。

指令重排序的原理:

重排指令,单线程下不影响程序执行逻辑,提高性能。

能保证线程安全吗

只能保证可见性

Java内存模型:

线程池:

核心思想:

通过线程复用来避免频繁的线程销毁和创建。

工作原理:

img

参数:

img

img