文章目录
0.1、线程中安全性问题的体现:0.2、线程安全问题的解决办法1、synchronized的底层实现原理分析2、Lock的底层实现原理分析?3、synchronized和volatile的区别?4、synchronized和Lock、ReentrantLock的区别?5、什么是AQS?0.1、线程中安全性问题的体现:
线程的安全性问题体现在三个方面:
(1)线程切换导致的原子性问题:
一个或者多个操作在 CPU 执行的过程中不被中断的特性。
(2)缓存导致的可见性问题:
一个线程对共享变量的修改,另外一个线程能够立刻看到。
(3)编译优化导致的有序性问题:
程序执行的顺序按照代码的先后顺序执行。
0.2、线程安全问题的解决办法
(1)JDK Atomic开头的原子类、synchronized、LOCK,可以解决原子性问题。
(2)synchronized、volatile、LOCK,可以解决可见性问题。
(3)Happens-Before 规则可以解决有序性问题。
1、synchronized的底层实现原理分析
Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
任何一个对象都有一个Monitor与之关联,当且一个Monitor被持有后,它将处于锁定状态。
Synchronized在JVM里的实现都是基于进入和退出Monitor对象来实现方法同步和代码块同步,虽然具体实现细节不一样,但是都可以通过成对的MonitorEnter和MonitorExit指令来实现。
(1)MonitorEnter指令:
插入在同步代码块的开始位置,当代码执行到该指令时,将会尝试获取该对象Monitor的所有权,即尝试获得该对象的锁;
(2)MonitorExit指令:
插入在方法结束处和异常处,JVM保证每个MonitorEnter必须有对应的MonitorExit;
2、Lock的底层实现原理分析?
Lock的底层是通过AQS实现的。什么是AOQ?(见下面第4部分)
底层是使用计数值、双向链表、CAS(乐观锁)+自旋。
底层存储结构就是:
int类型的状态值(用于锁的状态变更)+双向链表(用于存储等待中的线程)
(1)获取锁
基本原理还是通过乐观锁来实现的,通过修改int类型的state值作为锁状态值。通过自旋等待状态state被释放之后,就可以获取锁。
(2)释放锁
释放锁就是对AQS中的状态值State进行修改。同时更新下一个链表中的线程等待节点。
3、synchronized和volatile的区别?
volatile是一个类型修饰符,用来修饰被不同线程访问和修改的变量。可以说volatile是轻量级的synchronized。
volatie用来修饰变量,只能解决引发线程安全问题的由于缓存导致的可见性问题。
synchronized用来修饰方法或者方法块,同Lock都可以解决引发线程安全问题的线程切换导致的原子性问题和缓存导致的可见性问题。
被volatile修饰的变量,系统每次访问到它的时候都是直接从内存中提取,而不会利用缓存。所以使用volatile修饰的变量,所有线程在任何时候看到的变量的值都是相同的。
volatile可以解决可见性问题,但是不能解决原子性问题。而且使用volatile会降低程序的运行效率。所以除非迫不得已,不然不使用volatile。
4、synchronized和Lock、ReentrantLock的区别?
(1)关于本质
首先,synchronized是一个关键字,可以修饰方法和方法块。Lock是一个接口,ReentrantLock是Lock的一个实现类,提供了一些封装的方法。
(Lock lock = new ReentrantLock();)
(2)关于释放锁
synchronized在进行同步的时候,代码执行完毕或者产生异常后会自动释放锁。Lock需要手动在finally中释放锁,不然就会造成死锁。
(3)关于放弃锁
synchronized放弃锁只有两种情况:①线程执行完了同步代码块的内容②发生异常;
而lock不同,它可以设定超时时间,也就是说他可以在获取锁时便设定超时时间,如果在你设定的时间内它还没有获取到锁,那么它会放弃获取锁然后响应放弃操作。
(4)获取锁的状态
synchronized不能获取锁的状态,Lock可以获取锁的状态。
(5)使用场景
synchronized适用于少量代码同步,Lock适用于大量代码同步。
5、什么是AQS?
AQS:AbstactQueuedSynchronizer(队列同步器)
它是一个Java提供的底层同步工具类,为java中的并发同步组件提供统一的底层支持。例如ReentrantLock,CountdowLatch就是基于AQS实现的。
AQS的底层组成要素:
(1)使用Node实现虚拟的双向队列(FIFO:先进先出)
(2)int类型的变量state标识状态
过程:
线程会首先尝试获取锁,如果失败就将当前线程及等待状态等信息包装成一个node节点加入到同步队列sync queue里。
接着会不断的循环尝试获取锁,条件是当前节点为head的直接后继才会尝试。如果失败就会阻塞自己直到自己被唤醒。
而当持有锁的线程释放锁的时候,会唤醒队列中的后继线程。
如果觉得《java多线程:9 synchronized Lock的底层实现原理以及和volatile Lock ReentrantLock的区别?》对你有帮助,请点赞、收藏,并留下你的观点哦!