JUC
多线程:
多线程要注意哪些问题:
原子性:提供互斥访问
可见性:某个线程对主内存的修改可以及时被其他线程看到
有序性:由于指令重排
线程:
创建线程的方式,怎样启动,怎样停止
创建:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口与FutureTask(有点不好用)
- 使用线程池
启动:
Thread类的start()方法。
如何停止:
sleep()
interrupt()
线程的状态有哪些,blocked和waiting有什么区别:
状态有哪些:

blocked和waiting有什么区别:

主要是触发条件和唤醒条件。
虽然最终表现都是“线程挂起”,但 挂起的理由不同、唤醒的源头不同、重新运行前还要不要抢锁也不同。
sleep和wait的区别,sleep会释放cpu吗:
区别:

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都是实现类似管程的机制,他们的结构也可以一一对应上
锁的类别:
如何理解可重入锁:

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

如何理解公平锁和非公平锁:
公平锁:多个线程按照顺序来获取锁,线程直接加入队列中排队,只有队列中第一个线程才能获得锁。
非公平锁:多个线程枷锁时直接尝试获得锁,能抢到锁直接占有锁,抢不到才到等待队列中等待。
CAS:
什么是CAS:
一种乐观锁机制,包含三个操作数(内存位置,预期值,新值),如果内存V上的值等于预期值,就将其更新为新值,整个过程是原子性的,通过硬件支持来实现的。
CAS和AQS的关系:
CAS为AQS提供原子操作
CAS的缺点:
ABA问题
轮询消耗大
只能保证一个共享变量操作的原子性
为什么不能所有锁都用CAS:
高并发情况下,大量轮询会消耗系统资源
JAVA如何解决CAS的ABA问题的:
通过版本号的方法
synchronized:
实现原理:
在编译后会在同步代码块前后加上monitorenter和monitorexit。
维护waitSet和entryList:
- entryList
用来存放 因为抢锁失败而被阻塞 的线程(BLOCKED)。
新线程或重入失败的线程都会被放到这里,等待锁被释放后由 JVM 挑选一个唤醒。 - waitSet
只存放 已经持有锁,但在该锁上调用了obj.wait()的线程(WAITING/TIMED_WAITING)。
这些线程必须先拿到过锁,主动放弃锁并等待notify/notifyAll才会被放进去。
讲解下锁升级:
无锁->偏向锁->轻量级锁->重量级锁
- 无锁(001)
刚 new 出来的对象、计算过 hashCode 或偏向延迟未过前都处此状态。
不等于“没加锁”,只是尚未偏向任何线程。 - 偏向锁(101)
- 启动后 4 s(
-XX:BiasedLockingStartupDelay)内不会启用,所以“无锁→偏向锁”有延迟。 - 第一次加锁时把当前线程 ID 写入 mark word,之后该线程进出同步块不做任何 CAS,性能最高。
- 只要出现任何竞争(即使是同一线程重入以外的线程尝试查看锁),偏向立即撤销。
- 启动后 4 s(
- 轻量级锁(00)
- 撤销偏向后或第二个线程尝试加锁时进入。
- 原持有线程先把 mark word 拷贝到栈帧锁记录(Displaced Mark Word),后到的线程在自己栈帧里自旋 CAS。
- 自旋次数由
-XX:PreBlockSpin或自适应算法决定;自旋失败才膨胀。
- 重量级锁(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内存模型:
线程池:
核心思想:
通过线程复用来避免频繁的线程销毁和创建。
工作原理:

参数:

