【java】 如何自己写一把多线程锁 中 重写lock,trylock,unlok方法

【java】 如何自己写一把多线程锁 中 重写lock,trylock,unlok方法4.拿到unsafeimportsun.misc.Unsafe;importjava.lang.reflect.Constructor;importjava.lang.reflect.Field;importjava.lang.reflect.InvocationTargetException;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.locks.AbstractQueuedSynchronize

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

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/16  20: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 方法

trylock

整个流程如上 代码怎么写呢

首先 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;
    }

我们来测试一下 一个线程尝试加锁三次 试试锁重入

idea64_Z9hj6Q8cql

第一次调用trylock方法 status的值正好是0 修改成功 并且记录当前线程为锁的持有者 第二次循环尝试修改值失败 进入判断语句

当前线程正好是锁的持有者 于是把 status的值 +1 第三次循环依然如此

至此 trylock方法 搞定

6.搞定lock方法

lock方法和trylock方法的区别在于 trylock方法是尝试一下 获取到锁了就返回true 没有就返回false 不会阻塞在这等

而lock 方法获取到锁了就立即返回 没有获取到锁就一直等待 等待别唤醒后 继续抢锁 没有抢到继续等待

lock方法

首先创建一个等待队列 没抢到锁的线程进入等待队列等待

	//获取锁失败的线程的等待队列
    LinkedBlockingQueue<Thread> 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);
                }
            }
        }
    }

lock方法

7.搞定unlock方法

首先我们要搞清楚这个两个问题

  1. 谁去解锁?
  2. 解锁将产生什么变化呢?

谁去解锁 当然是锁的主人 也就是 master_thred 记录的线程

解锁将产生什么变化 锁说到底就是判断一个标记位 它的不同状态代表着锁的不同状态 也就是改变 status的值 然后看下等待队列中是否有其他等待的线程 唤醒它们

unlock方法

搞清楚这两点后 开始写代码

 @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 方法 是不是就没问题呢?

留着大家测试 欢迎评论区留言

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/181625.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • android activitymanagerservice_安卓开发API

    android activitymanagerservice_安卓开发APIAndroid中Java层的ActivityManager类中封装了很多API,可以供我们查询当前系统的很多信息,包括:内存、进程(Process)、任务栈(Task)、服务(Service)等的相关信息。利用这些信息可以进行一些有用的判断,例如判断当前系统内存是否不足、指定Service是否在运行中。(ActivityManager类封装了很多API方法供上层调用,具体负责管理Activity、Service等组件的是ActivityManagerService(AMS…

  • 宽字节注入原理学习

    宽字节注入原理学习0x01开篇本题用到考点是宽字节注入,遇到这种注入类型学习记录。推荐两篇链接:浅析白盒审计中的字符编码及SQL注入|离别歌Von的博客|VonBlog为方便自我下次忘记,总结一下:1.宽字节涉及到编码问题,便于理解需要看一看2.宽字节注入现在已经很少见,因为如今的编码大多使用utf-8常见url编码:空格–%20′–%27#–%23\–%5c0x02原理我们注入时都会简单输入一个’或者”,进行测试,如果数据库过滤不严格就会产生报错

    2022年10月14日
  • 在VMware下安装中标麒麟操作系统7.0以及Neokylin基础常用知识「建议收藏」

    在VMware下安装中标麒麟操作系统7.0以及Neokylin基础常用知识「建议收藏」文章目录环境介绍:开始安装Neokylin7.0:Neokylin基础常用知识一.关机、重启命令二.查询文件列表三.目录切换四.查看磁盘信息五.目录结构六.文件类型七.文件管理八.用户和用户组管理九.权限管理环境介绍:虚拟机::VMwareWorkstationPro15中标麒麟操作系统镜像:nsV7Update6-adv-lic-build4-x86_64.iso镜像文件下载链接提取码:277k开始安装Neokylin7.0:首先,在虚拟机主页上点击“创建一个新

  • linux查看80端口占用情况_centos如何查看端口是否被占用

    linux查看80端口占用情况_centos如何查看端口是否被占用前言平常使用linux,我们经常需要查看哪个服务占用了哪个端口,接下来就为大家介绍了2种Linux查看端口占用情况可以使用lsof和netstat命令。1.lsof-i:端口号用

  • eXtremeDB微秒级实时数据库简介「建议收藏」

    eXtremeDB微秒级实时数据库简介「建议收藏」eXtremeDB微秒级实时数据库简介 eXtremeDB实时数据库是美国McObject公司于上世纪九十年代末推出的全世界第一款全内存式实时数据库,特别为高性能、低开销、稳定可靠的极速实时数据管理而设计。 eXtremeDB的性能可以达到微秒一级的惊人速度。eXtremeDB能够达到这样惊人的极限速度,是由其对市场的独特理解、长期的行业经验、持续不断的创新精神和革命性的体系结构等…

  • 高并发的解决方案「建议收藏」

    高并发的解决方案「建议收藏」1.应用和静态资源分离刚开始的时候应用和静态资源是保存在一起的,当并发量达到一定程度的时候就需要将静态资源保存到专门的服务器中,静态资源主要包括图片、视频、js、css和一些资源文件等,这些文件因为没有状态所以分离比较简单,直接存放到响应的服务器就可以了,一般会使用专门的域名去访问。通过不同的域名可以让浏览器直接访问资源服务器而不需要再访问应用服务器了。架构图如下:2.页面缓存

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号