先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
}
输出结果,从这里可以看到 Thread-0 锁定期间,Thread-1 的读操作不受影响:
测试 读锁-写锁
相互阻塞:
@Slf4j
public class TestReadWriteLock {
public static void main(String[] args) throws InterruptedException {
Datacontainer datacontainer = new Datacontainer();
new Thread(() -> {
datacontainer.read();
}, “t1”).start();
Thread.sleep(100);
new Thread(() -> {
datacontainer.write();
}, “t2”).start();
}
}
结果:
注意:
- 读锁不支持条件变量
- 重入时升级不支持:即持有读锁的情况下去获取写锁,会导致获取写锁永久等待
- 重入时降级支持:即持有写锁的情况下去获取读锁
看一个重入降级获得锁栗子:
class CachedData {
Object data;
// 是否有效,如果失效,需要重新计算 data
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// 获取写锁前必须释放读锁
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// 判断是否有其它线程已经获取了写锁、更新了缓存, 避免重复更新
if (!cacheValid) {
data = …
cacheValid = true;
}
// 降级为读锁, 释放写锁, 这样能够让其它线程读取缓存
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock();
}
}
// 自己用完数据, 释放读锁
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}
1.2 原理演示
读写锁用的是同一个 Sycn 同步器,因此等待队列、state 等也是同一个。
t1 w.lock,t2 r.lock
1) t1 成功上锁,流程与 ReentrantLock 加锁相比没有特殊之处,不同是写锁状态占了 state 的低 16 位,而读锁使用的是 state 的高 16 位
2)t2 执行 r.lock
,这时进入读锁的 sync.acquireShared
(1) 流程,首先会进入 tryAcquireShared
流程。如果有写锁占据,那么 tryAcquireShared 返回 -1 表示失败
tryAcquireShared 返回值表示:
- -1 表示失败
- 0 表示成功
- 正数表示成功(而且数值是还有几个后继节点需要唤醒,读写锁返回1)
3)这时会进入 sync.doAcquireShared
(1) 流程,首先也是调用 addWaiter
添加节点,不同之处在于节点被设置为 Node.SHARED
模式而非 Node.EXCLUSIVE 模式,注意此时 t2 仍处于活跃状态
4)t2 会看看自己的节点是不是老二,如果是,还会再次调用 tryAcquireShared
(1) 来尝试获取锁
5)如果没有成功,在 doAcquireShared 内 for (;;)
循环一次,把前驱节点的 waitStatus 改为 -1,再 for (;😉 循环一 次尝试 tryAcquireShared(1) 如果还不成功,那么在 parkAndCheckInterrupt
() 处 park
t3 r.lock,t4 w.lock
这种状态下,假设又有 t3 加读锁和 t4 加写锁,这期间 t1 仍然持有锁,就变成了下面的样子
t1 w.unlock
这时会走到写锁的 sync.release
(1) 流程,调用 sync.tryRelease
(1) 成功,变成下面的样子
接下来执行唤醒流程 sync.unparkSuccessor
,即让老二恢复运行,这时 t2 在 doAcquireShared
内 parkAndCheckInterrupt
处恢复运行 这回再来一次 for (;;)
执行 tryAcquireShared
成功则让读锁计数加一
这时 t2 已经恢复运行,接下来 t2 调用 setHeadAndPropagate
(node, 1),它原本所在节点被置为头节点
事情还没完,在 setHeadAndPropagate
方法内还会检查下一个节点是否是 shared
,如果是则调用 doReleaseShared
() 将 head 的状态从 -1 改为 0 并唤醒老二,这时 t3 在 doAcquireShared
内 parkAndCheckInterrupt
处恢复运行
这回再来一次 for (;;)
执行 tryAcquireShared
成功则让读锁计数加一
这时 t3 已经恢复运行,接下来 t3 调用 setHeadAndPropagate
(node, 1),它原本所在节点被置为头节点
下一个节点不是 shared
了,因此不会继续唤醒 t4 所在节点
t2 r.unlock,t3 r.unlock
t2 进入 sync.releaseShared
(1) 中,调用 tryReleaseShared
(1) 让计数减一,但由于计数还不为零
t3 进入 sync.releaseShared
(1) 中,调用 tryReleaseShared
(1) 让计数减一,这回计数为零了,进入 doReleaseShared
() 将头节点从 -1 改为 0 并唤醒老二,即
之后 t4 在 acquireQueued
中 parkAndCheckInterrupt
处恢复运行,再次 for (;;)
这次自己是老二,并且没有其他竞争,tryAcquire
(1) 成功,修改头结点,流程结束
2.1 概述
该类自 JDK 8 加入,是为了进一步优化读性能,它的特点是在使用读锁、写锁时都必须配合【戳】使用
加解读锁:
long stamp = lock.readLock();
lock.unlockRead(stamp);
加解写锁:
long stamp = lock.writeLock();
lock.unlockWrite(stamp);
乐观读:StampedLock
支持 tryOptimisticRead()
方法(乐观读),读取完毕后需要做一次戳校验如果校验通过,表示这期间确实没有写操作,数据可以安全使用,如果校验没通过,需要重新获取读锁,保证数据安全。
long stamp = lock.tryOptimisticRead();
// 验戳
if(!lock.validate(stamp)){
// 锁升级
}
2.2 例子
提供一个 数据容器类 内部分别使用读锁保护数据的 read() 方法,写锁保护数据的 write() 方法:
@Slf4j
class DataContainerStamper {
private int data;
private final StampedLock lock = new StampedLock();
public DataContainerStamper(int data) {
this.data = data;
}
public int read(int readTime) {
//乐观读
long stamp = lock.tryOptimisticRead();
log.debug(“optimistic read lockuing … {}”, stamp);
//模拟读取时间
Sleeper.sleep(readTime);
//校验戳,通过直接返回
if (lock.validate(stamp)) {
log.debug(“read finish… {}”, stamp);
return data;
}
//锁的升级->读锁
log.debug(“updating to read lock… {}”, stamp);
try {
//获取读锁
stamp = lock.readLock();
log.debug(“read lock {}”, stamp);
Sleeper.sleep(readTime);
log.debug(“read finish… {}”, stamp);
return data;
} finally {
log.debug(“read unlock {}”, stamp);
独家面经总结,超级精彩
本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家!
Java面试准备
准确的说这里又分为两部分:
- Java刷题
- 算法刷题
Java刷题:此份文档详细记录了千道面试题与详解;
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
0pZwCy5-1713439593930)]
[外链图片转存中…(img-cGghYUoH-1713439593931)]
Java面试准备
准确的说这里又分为两部分:
- Java刷题
- 算法刷题
Java刷题:此份文档详细记录了千道面试题与详解;
[外链图片转存中…(img-B7zaQU1N-1713439593932)]
[外链图片转存中…(img-NpRWebvS-1713439593933)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-umayBP9Z-1713439593934)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!