1.syncronized底层原理——悲观锁
synchronized有对象锁和类锁两种,多个线程中只有一个线程可以获取对象锁,其他线程都会处于阻塞状态
synchronized是底层是基于monitor实现的。monitor是C++编写的jvm对象,主要分为owner(这个只会存一个线程的信息,记录当前锁被哪个线程获取了)、entrySet(这个是一个队列,记录没有抢到锁的线程,他们都是处于block状态的)、waitSet(记录调用了wait方法的线程)
monitor属于重量级锁,在jdk1.6之后又引入了偏向锁和轻量级锁
(更难的听不懂了,涉及JVM)
2.谈谈JMM(java memory model)java内存模型
JMM定义共享内存中,多线程程序的读写规范。JMM把内存分为工作内存和主内存;线程和线程之间是隔离的,但是可以通过线程1把消息从工作内存发送给主内存,再由主内存把消息推送给线程2,实现线程之间的交互
个人理解工作内存≈栈内存Stack,而主内存≈堆内存Heap
3.谈谈CAS(compare and swap)——乐观锁 | 自旋锁
cas是一种乐观锁思想,在无锁情况下保证线程操作共享数据的原子性。
java中的cas是基于操作系统的cas实现的,属于native方法;工作内存从主内存读取到的数据,我们需要把旧数据修改。当要修改时我们会再次比对工作内存的旧数据和主内存的数据,如果一致,那么就可以修改;如果不一致,那就要发生自旋:工作内存再次去主内存读取数据并重复上述方法。
在锁竞争不激烈的情况下cas是比synchronized效率要好的
4.乐观锁和悲观锁的区别
乐观锁允许别的线程修改变量,如果别的线程修改了变量那就自旋
悲观锁不允许别的线程修改变量,每次上锁的时候别的线程都会阻塞
5.关键字volatile
可见性:在线程中使用volatile修饰共享变量,可以防止编译器优化的发生。
下述例子中,下方的线程while(flag)被JIT(即时编译器)优化为了while(true),从而导致上面的线程修改flag下面的线程却读不到结果,这时我们可以给flag添加volatile来阻止编译器优化
static boolean flag=true; public static void main(String[] args) throws InterruptedException { new Thread(()->{ try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } flag=false; System.out.println(flag+"已经修改"); }).start(); new Thread(()->{ int i=0; while (flag){ i++; } System.out.println(flag+"_"+i); }).start(); }
禁止指令重排序:阻止一些读写操作越过屏障发生一些意外问题(写是防止当前指令越过上方指令,读是防止当前指令越过下方指令)
6.什么是AQS
AQS是抽象队列同步器,与synchronized不同的是他是由java实现的,并且锁激烈竞争的情况下有多种解决方案而不是像synchronized一样只有重量级锁
通过volatile保证state的可见性,然后当某一线程持有state后改变其数值,后续等待线程进入队列,依据队列完成排队
如何保证原子性:利用CAS在抢state时保证原子性
7.什么是公平锁:
新来的线程和队列中等待的线程争抢锁资源
非公平锁:
新来的线程只能进入队列的tail,只有队列的head能获取锁资源
注意,一般情况下公平锁的效率没有非公平锁高,公平锁的吞吐量较低
8.ReentrantLock可重入锁的原理
reentrantLock是基于AQS队列+CAS实现的,有无参构造函数和是否使用公平锁的构造函数,其中无参情况下默认是false
9.synchronized和lock的区别
- 语法上:synchronized是关键字,底层由C++实现,来自jvm;lock是接口,底层java原生,来自jdk。
- 功能上:都是悲观锁,互斥同步;lock有比synchronized更丰富的功能,lock除了正常锁以外还有公平锁,可打断锁,超时锁,多条件锁(这个类似于await和notify),并且读写锁就是是lock。但是synchronized在退出代码块后会自动释放,而lock需要手动调用unlock来释放锁。
- 性能上:竞争不激烈的时候synchronized因为有偏向锁和轻量级锁优于lock,在竞争激烈的情况下lock具有更好的性能
10.死锁的产生条件
一个线程同时获取多把锁
如何检查死锁的发生:
- 先用jps查看当前进程,进程里如果有死锁会显示xxx线程为deadlock,再用jstack查看详细信息
- 在jdk目录中找jconsole,此时能选择你在jps里查看的所有进程
11.讲讲concurrentHashMap
JDK1.7使用segment数组+hashmap实现了concurrenthashmap;通过对key的hash计算找到对应的segment数组,然后通过reentrantLock锁住具体的segment数组,高并发的时候会通过CAS自旋锁来保证数据安全,然后再通过hash定位具体位置。劣势就是多个key如果hash相同的话他们就会阻塞。segment数组不能进行扩容,这也导致1.7的concurrenthashmap的性能不好
JDK1.8版本下采用CAS和synchronized对HashMap进行了增强。
利用CAS来控制扩容的实现,为头结点上锁,暂停其他线程的put操作,避免了多线程的插入冲突;在空位置新增节点时,也会采用CAS确保并发下能插入成功
synchronized只锁链表或者红黑树的首节点,保证数组节点只能被一个线程修改,降低了锁的颗粒度并且只要hash不冲突就不会有效率问题;在扩容时通过synchronized保证了数据迁移的一致性和完整性
12.java如何保证多线程的执行安全
原子性:一个线程在CPU中的操作是不可暂停的,是无法中断的。这里我们是通过锁解决,保证数据正确
可见性:一个线程对共享变量的修改对另一个线程可见。这里我们使用volatile保证可见性
有序性:处理器为了提高程序运行效率,对输入代码进行优化,不能保证程序中各个语句的执行顺序同代码中的顺序一致,但是能保证程序最终执行结果和代码顺序执行的结果一致。这里我们使用volatile保证有序性
标签:java,速通,synchronized,lock,问题,flag,线程,内存 From: https://www.cnblogs.com/kun1790051360/p/18470932