1.3.1 偏向锁
在一个线程在执行获取锁的时候,当前线程会在monitor
对象中存储指向该线程的ID。当线程再次进入的时候,不需要通过CAS的方法再来进行加锁或者解锁,而是检测偏向锁的ID是不是当前要进行的线程,如果是,直接进入。
偏向锁,适用于一个线程执行任务的情况
在JDK1.6
中,默认是开启的。可以通过-XX:-UseBiasedLocking=false
参数关闭偏向锁
1.3.2 轻量级锁
轻量级锁是指锁为偏向锁的时候,该锁被其他线程尝试获取,此时偏向锁升级为轻量级锁,其他线程会通过自旋的方式尝试获取锁,线程不会阻塞,从而提供性能
升级为轻量级锁的情况有两种:
-
关闭偏向锁
-
有多个线程竞争偏向锁的时候
具体实现:
线程进行代码块以后,如果同步对象锁状态为无锁的状态,虚拟机将首先在当前线程的栈帧中创建一个锁记录的空间。这个空间内存储了当前获取锁的对象。
使用情况:
两个线程的互相访问
1.3.3 重量级锁
在有超过2个线程访问同一把锁的时候,锁自动升级为重量级锁,也就是传统的synchronized
,此时其他未获取锁的线程会陷入等待状态,不可被中断。
由于依赖于monitor
指令,所以其消耗系统资源比较大
上面的三个阶段就是锁升级的过程
1.3.4 锁粗化
当在一个循环中,我们多次使用对同一个代码进行加锁,这个时候,JVM会自动实现锁粗化,即在循环外进行添加同步代码块。
代码案例:
锁粗化之前:
for (int i = 0; i < 10; i++) {
synchronized (LockBigDemo.class){
System.out.println();
}
}
锁粗化之后:
synchronized (LockBigDemo.class){
for (int i = 0; i < 10; i++) {
System.out.println();
}
}
本次关于synchronized
的底层原理没有以代码的方式展开,之后笔者会出一篇synchronized
底层原理剖析的文章
2. Lock锁
一个类级别的锁,需要手动释放锁。可以选择性的选择设置为公平锁或者不公平锁。等待线程可以被打断。
底层是基于AQS
+AOS
。AQS
类完成具体的加锁逻辑,AOS
保存获取锁的线程信息
2.1 ReentrantLock
我们以ReentrantLock
为例解析一下其加锁的过程。
2.1.1 lock方法
首先通过ReentrantLock
的构造方法的布尔值判断创建的锁是公平锁还是非公平锁。
假设现在创建的是非公平锁,他首先会判断锁有没有被获取,如果没有被获取,则直接获取锁;
如果锁已经被获取,执行一次自旋,尝试获取锁。
如果锁已经被获取,则将当前线程封装为AQS
队列的一个节点,然后判断当前节点的前驱节点是不是HEAD
节点,如果是,尝试获取锁;如果不是。则寻找一个安全点(线程状态位SIGNAL=-1
的节点)。
开始不断自旋。判断前节点是不是HEAD
节点,如果是获取锁,如果不是挂起。
源码解读:
- 非公平锁
lock
final void lock() {
//判断是否存在锁
if (compareAndSetState(0, 1))
//获取锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//非公平锁的自旋逻辑
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取锁状态
int c = getState();
//如果锁没被获取,获取锁
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//当前线程已经获取到了锁
else if (current == getExclusiveOwnerThread()) {
//线程进入次数增加
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error(“Maximum lock count exceeded”);
setState(nextc);
return true;
}
return false;
}
//将线程封装为一个线程节点,传入锁模式,排他或者共享
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 获取尾节点
Node pred = tail;
//如果尾节点不为Null,直接将这个线程节点添加到队尾
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//为空,自旋设置尾节点
enq(node);
return node;
}
private Node enq(final Node node) {
for (;😉 {
Node t = tail;
//初始化
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
//将头结点和尾结点都设置为当前节点
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
//尝试入队
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
《MySql面试专题》
《MySql性能优化的21个最佳实践》
《MySQL高级知识笔记》
文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图
关注我,点赞本文给更多有需要的人
)]
[外链图片转存中…(img-IEYUvPEJ-1710752669219)]
[外链图片转存中…(img-aa9lAZVx-1710752669220)]
[外链图片转存中…(img-WvG9BnFn-1710752669220)]
[外链图片转存中…(img-d8y4Yqrj-1710752669220)]
[外链图片转存中…(img-OzxTxiQW-1710752669220)]
[外链图片转存中…(img-mdBedJb5-1710752669221)]
文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图
[外链图片转存中…(img-uvhaXOLr-1710752669221)]
关注我,点赞本文给更多有需要的人