美文网首页程序员
如何用Java模拟一把锁

如何用Java模拟一把锁

作者: 麦德漂 | 来源:发表于2019-02-27 15:23 被阅读46次

我们常用到加锁方式有synchronized关键字,还有ReentrantLock,那如何利用Java模拟一把锁。

开始构思:

加锁就是为了让任何时刻,都只有一个线程访问共享资源,如果共享资源已经被线程占用,那其他线程来了只能选择等待,占用线程释放资源以后再唤醒其他等待线程。于是锁的基本样子就呼之欲出了,简单粗暴:

public synchronized void lock() {

    try {

        wait();

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

}

public synchronized void unlock() {

    notifyAll();

}

}

创建一个包含上锁,解锁方法的Java类,上锁的时候思路就是等待,解锁的时候思路就是唤醒,大概的模样就有了。注意这里上锁、解锁方法都使用synchronized修饰,请读者思考下为什么?

逻辑推翻,代码优化

上面我们实现的锁无法使用,上锁方法一进来就等待,那所有线程都处于等待状态了,没有线程可以获取锁,显然是错误的。

推翻了自己,我们继续构思并优化上述代码。当有线程进入时,我们需要检查资源是否已上锁,如果是,线程等待,否则,线程占有资源并标记资源已上锁。于是,我们需要增加一个锁标记,用来标识资源是否已被占用:

        private boolean locked = false;

        public synchronized void lock() {

            try {

                while(locked) {

                    wait();

                }

            locked = true;

        } catch (InterruptedException e) {

            e.printStackTrace();

    }

}

public synchronized void unlock() {

        locked = false;

        notifyAll();

}

优化后代码貌似可以基本使用,我们增加了一个锁标记,当有线程上锁时,将锁标记置为true,其他线程等待,当线程释放锁时,将锁的状态置为false。注意释放锁的时候要先设置锁的状态,然后再唤醒等待线程。

逻辑优化,代码优化

我们的锁现在基本可以正常使用了,对于同一个对象实例,我们可以成功完成上锁与解锁,但是同一个线程继续获取锁时,还是会继续等待,换句话说,我们的锁不支持可重入。

我们继续构思,可重入是指当一个线程获取对象锁时,这个线程再次访问对象同步共享资源时,可以直接进入,不需要再次获取锁。于是我们需要记录一下当前占有锁的线程:

private boolean locked = false;

private Thread locker = null;

public synchronized void lock() {

        try {

            Thread currentThread = Thread.currentThread();

            while(locked && locker!=currentThread) {

                wait();

            }

            locked = true;

            locker = currentThread;

        } catch (InterruptedException e) {

            e.printStackTrace();

    }

}

public synchronized void unlock() {

    locked = false;

    notifyAll();

}

当有线程占有资源时,我们修改锁的标记并且记录占有锁的线程,当线程再次请求锁时,如果请求线程和占有锁线程是同一个线程,则可以继续访问同步共享资源,无需再次获取锁,这样就实现了可重入锁的功能。

小记:

我们基本实现了一把可重入锁,但是可重入锁的获取次数我们没有记录,释放锁的时候,我们粗鲁的唤醒所有线程。so减少无用的唤醒操作次数,严谨的执行唤醒操作,我们可以加入一个锁count计数,当线程重入锁时,执行计数incr,锁释放时,执行计数decr,当执行释放计数为0时,执行唤醒操作。请读者自行思考下如何实现?

相关文章

  • 如何用Java模拟一把锁

    我们常用到加锁方式有synchronized关键字,还有ReentrantLock,那如何利用Java模拟一把锁。...

  • 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲

    最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 在Java并发场景中,会涉及到各种各样的锁如...

  • 总结

    1、如何用Zookeeper实现分布式锁? 链接 2、漫画:什么是分布式锁? 传送门 3、Java并发专题【8】初...

  • Redis分布式锁

    Jedis的nx生成锁 如何删除锁 模拟抢单动作(10w个人开抢) jedis的nx生成锁 对于java中想操作r...

  • AQS Something

    AQS: java 锁的核心抽象类: 主要的思想,就是 violate 的 state + cas 来模拟,加锁 ...

  • 线程 3. 锁对象

    什么是锁对象?每个java对象都有一个锁对象.而且只有一把钥匙. 如何创建锁对象:可以使用this关键字作为锁对象...

  • java中的锁

    Java锁的种类以及辨析锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronize...

  • Java并发之如何用一把锁保护多个资源(互斥锁,下)

    上篇文章中提到受保护的资源和锁之间合理的关系应该是 N:1 的关系,也就是说用一把锁来保护多个资源。分两种情况: ...

  • 在Java中如何用一把锁保护多个资源?

    在上一篇文章中,我们提到受保护资源和锁之间合理的关联关系应该是 N:1 的关系,也就是说可以用一把锁来保护多个资源...

  • 锁对象改变引发的线程问题

    Java多线程锁对象的改变 用lock获取锁对象,当lock被修改以后,会产生是一把新的锁,另一个线程获取锁对象时...

网友评论

    本文标题:如何用Java模拟一把锁

    本文链接:https://www.haomeiwen.com/subject/fbrfuqtx.html