如何用Java模拟一把锁

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

 

开始构思:

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

public class MyLockDemo {

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时,执行唤醒操作。请读者自行思考下如何实现?

喜欢请关注微信公众号:码农小麦

posted @ 2019-02-27 17:06  码农小麦  阅读(431)  评论(0编辑  收藏  举报