4.拿到unsafe
import sun.misc.Unsafe;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author sz
* @DATE 2022/3/1620:30
*/
public class MyReentrantLock implements Lock {//记录锁状态0 ->锁可用1-> 锁被占>1->锁重入
private int status = 0;
private long offset = unsafe.objectFieldOffset(MyLock.class.getDeclaredField("status"));
private static Unsafe unsafe;
static {
try {
unsafe = getUnsafe();
} catch (Exception e) {
e.printStackTrace();
}
}public MyReentrantLock() throws NoSuchFieldException {
}public static Unsafe getUnsafe() throws Exception {
//利用反射
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
// 设置为可见
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
// 获取Unsafe对象
return unsafe;
}@Override
public void lock() {
new ReentrantLock().lock();
}@Override
public void unlock() {}@Override
public boolean tryLock() {
return false;
}@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}@Override
public void lockInterruptibly() throws InterruptedException {}@Override
public Condition newCondition() {
return null;
}
}
接下来就能为所欲为了 哈哈
5.搞定 trylock 方法
文章图片
整个流程如上 代码怎么写呢
【多线程|【java】 如何自己写一把多线程锁 中 重写lock,trylock,unlok方法】首先 if判断 尝试修改 status的值
if (unsafe.compareAndSwapInt(this,offset,0,1))
offset ==> status 在内存中的偏移量
如果status的值是0,就改成1 并且 返回 true 否则返回false
返回true 记录下当前锁是那个线程持有的 以后解锁要用 谁家的锁谁来开
//将当前锁的主人设置为当前线程
master_thred = Thread.currentThread();
return true;
如果 尝试修改status的值 失败 判断是不是锁重入 如果是锁重入 将status的值 +1 并且返回true
//判断是否锁重入
if (Thread.currentThread()==master_thred){
//如果是锁重入状态值 +1
unsafe.getAndAddInt(this,offset,1);
//锁重入成功
return true;
如果上面条件都不满足 直接返回 false
于是tryLock 方法重写后就是这个样子
@Override
public boolean tryLock() {//如果 status的值是 0没有人用锁改成1
if (unsafe.compareAndSwapInt(this,offset,0,1)){
//将当前锁的主人设置为当前线程
master_thred = Thread.currentThread();
return true;
}else {
//判断是否锁重入
if (Thread.currentThread()==master_thred){
//如果是锁重入状态值 +1
unsafe.getAndAddInt(this,offset,1);
//锁重入成功
return true;
}
}return false;
}
我们来测试一下 一个线程尝试加锁三次 试试锁重入
第一次调用trylock方法 status的值正好是0 修改成功 并且记录当前线程为锁的持有者 第二次循环尝试修改值失败 进入判断语句
当前线程正好是锁的持有者 于是把 status的值 +1 第三次循环依然如此
至此 trylock方法 搞定
6.搞定lock方法 lock方法和trylock方法的区别在于 trylock方法是尝试一下 获取到锁了就返回true 没有就返回false 不会阻塞在这等
而lock 方法获取到锁了就立即返回 没有获取到锁就一直等待 等待别唤醒后 继续抢锁 没有抢到继续等待
文章图片
首先创建一个等待队列 没抢到锁的线程进入等待队列等待
//获取锁失败的线程的等待队列
LinkedBlockingQueue waitQueue = new LinkedBlockingQueue<>();
@Override
public void lock() {//如果没有获取到锁
if (!tryLock()){
//将当前线程加入等待队列
waitQueue.add(Thread.currentThread());
//循环不停抢锁
while (true){
//不停尝试抢锁
if (tryLock()){
//抢到了从队列中剔除
//poll() 检索并删除此队列的头部,如果此队列为空,则返回 null 。
waitQueue.poll();
//跳出循环
break;
}else {
//没有抢到阻塞等待等待被唤醒
unsafe.park(false,0);
}
}
}
}
文章图片
7.搞定unlock方法 首先我们要搞清楚这个两个问题
- 谁去解锁?
- 解锁将产生什么变化呢?
解锁将产生什么变化 锁说到底就是判断一个标记位 它的不同状态代表着锁的不同状态 也就是改变 status的值 然后看下等待队列中是否有其他等待的线程 唤醒它们
文章图片
搞清楚这两点后 开始写代码
@Override
public void unlock() {
//首先判断锁的持有者是不是当前线程
if (Thread.currentThread() != master_thred) {
//前朝的剑怎么斩本朝的官
throw new RuntimeException("释放锁失败,当前线程:" + Thread.currentThread() + "未持有锁");
}
//能过前面的if判断说明当前锁是被人占用的且是当前线程占有的
//修改status的值
if (unsafe.getAndAddInt(this, offset, -1) > 0) {if (unsafe.getInt(this, offset) == 0) {
//如果当前status==0也就是没有线程持有锁了
master_thred = null;
//再从等待队列中拿出等待线程
if (waitQueue.size()!=0) {
//注意这个 peek 方法不会把线程从队列中删除因为即时唤醒也有可能拿不到锁
//真正从队列中删除要等到 抢到锁了调用 poll 方法
Thread peek = waitQueue.peek();
//唤醒线程
if (peek != null) {
unsafe.unpark(peek);
}
}
}} else {
//重置锁
unsafe.putInt(this, offset, 0);
//锁已经被释放了抛出异常
throw new RuntimeException("释放锁失败,锁已经被释放");
}
}
到此 重写了 lock接口的 trylock方法 lock方法 与unlock 方法 是不是就没问题呢?
留着大家测试 欢迎评论区留言
推荐阅读
- 蓝桥杯|蓝桥杯AcWing学习笔记 4-3排序的学习(附相关蓝桥真题(小朋友排队)(Java))
- #|蓝桥杯31天冲刺打卡题解(Day10)
- #|蓝桥杯31天冲刺打卡题解(Day4)
- #|蓝桥杯31天冲刺打卡题解(Day11)
- Java14新特性及代码示例
- Kafka 怎么顺序消费(面试必备。。。)
- Java12新特性及代码示例
- C#项目实战|C#爬虫项目(SWorld阅读)
- Java|Java面试突击系列(十二)(数据库分库分表的面试连环炮)