一起备战面试吧,也是巩固,不再害怕面试
notifyAll:唤醒所有wait的线程
notify:只随机唤醒一个 wait 线程
共同点
不同点
有三种方式可以停止线程
每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针
具体的流程:
Java中的synchronized有偏向锁、轻量级锁、重量级锁三种形式,分别对应了锁只被一个线程持有(偏向锁)、不同线程交替持有锁(轻量级锁)、多线程竞争锁(重量级锁)三种情况。
描述 | |
---|---|
重量级锁 | 底层使用的Monitor实现,里面涉及到了用户态和内核态的切换、进程的上下文切换,成本较高,性能比较低。 |
轻量级锁 | 解决重量级锁的弊端线程加锁的时间是错开的(也就是没有竞争),可以使用轻量级锁来优化。轻量级修改了对象头的锁标志,相对重量级锁性能提升很多。每次修改都是CAS操作,保证原子性 |
偏向锁 | 解决轻量级锁在同一个线程需要多次CAS操作的问题,一段很长的时间内都只被一个线程使用锁,可以使用了偏向锁,在第一次获得锁时,会有一个CAS操作,之后该线程再获取锁,只需要判断mark word中是否是自己的线程id即可,而不是开销相对较大的CAS命令 |
全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架,它是构建锁或者其他同步组件的基础框架
synchronized | AQS |
---|---|
关键字,c++ 语言实现 | java 语言实现 |
悲观锁,自动释放锁 | 悲观锁,手动开启和关闭(cancelAcquire()来设置状态为0) |
锁竞争激烈都是重量级锁,性能差 | 锁竞争激烈的情况下,提供了多种解决方案 |
AQS常见的实现类
在去修改state状态的时候**,使用的cas自旋锁来保证原子性**,确保只能有一个线程修改成功,修改失败的线程将会进入FIFO队列(addWaiter(Node.EXCLUSIVE))中等待
比较典型的AQS实现类ReentrantLock,它默认就是非公平锁,新的线程与队列中的线程共同来抢资源
ReentrantLock翻译过来是可重入锁,相对于synchronized它具备以下特点:
ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似
死锁:一个线程需要同时获取多把锁,这时就容易发生死锁
例如:
t1 线程获得A对象锁,接下来想获取B对象的锁
t2 线程获得B对象锁,接下来想获取A对象的锁
当程序出现了死锁现象,我们可以使用jdk自带的工具:jps和 jstack
其他解决工具,可视化工具
用于对jvm的内存,线程,类 的监控,是一个基于 jmx 的 GUI 性能监控工具
打开方式:java 安装目录 bin目录下 直接启动 jconsole.exe 就行
能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈
打开方式:java 安装目录 bin目录下 直接启动 jvisualvm.exe就行
ConcurrentHashMap 是一种线程安全的高效Map集合
底层数据结构:
在JDK1.8中,放弃了Segment臃肿的设计,数据结构跟HashMap的数据结构是一样的:数组+红黑树+链表
采用 CAS + Synchronized来保证并发安全进行实现
Java并发编程三大特性
解决方案:
1.synchronized:同步加锁
2.JUC里面的lock:加锁
解决方案: