[java] How to write a multi-threaded lock to rewrite lock, trylock, unlock methods

4. Get 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;
    }
}

Then you can do whatever you want haha

5. Get the trylock method

trylock

How to write the whole process as above code?

First, if judgment tries to modify the value of status

if (unsafe.compareAndSwapInt(this,offset,0,1))

offset ==> status offset in memory

If the value of status is 0, change it to 1 and return true, otherwise return false

Return true to record the current lock is held by which thread and who will use the lock to unlock it later

//将当前锁的主人设置为当前线程
            master_thred = Thread.currentThread();
            return true;

If the attempt to modify the value of status fails to determine whether the lock is reentrant, if it is the lock reentrancy, the value of status will be +1 and return true

  //判断是否锁重入
            if (Thread.currentThread()==master_thred){
                //如果是锁重入  状态值 +1
                unsafe.getAndAddInt(this,offset,1);
                //锁重入成功
                return true;

If none of the above conditions are met, return false directly

So the tryLock method is rewritten like this

 @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;
    }

Let's test a thread trying to lock three times and try lock reentrancy

idea64_Z9hj6Q8cql

The first time the trylock method is called, the value of status is exactly 0, the modification is successful and the current thread is recorded as the holder of the lock. The second loop attempts to modify the value and fails to enter the judgment statement

The current thread happens to be the holder of the lock, so the value of status is +1 for the third cycle and the same is true

So far trylock method is done

6. Get the lock method

The difference between the lock method and the trylock method is that the trylock method is to try to acquire the lock and return true, if not, it will return false, and it will not block here.

And the lock method returns immediately after acquiring the lock, and waits until the lock is not acquired, and continues to wait until it wakes up and continues to grab the lock.

lock method

First create a waiting queue for a thread that does not grab the lock and enter the waiting queue to wait

	//获取锁失败的线程的等待队列
    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 method

7. Get the unlock method

First, we need to clarify these two issues

  1. Who will unlock it?
  2. How will unlocking change?

Whoever unlocks is, of course, the owner of the lock, which is the thread recorded by master_thred

What changes will unlocking locks? In the end, it is to judge a flag bit and its different states represent different states of the lock, that is, to change the value of status and then see if there are other waiting threads in the waiting queue to wake them up

unlock method

After figuring out these two points, start writing code

 @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("释放锁失败,锁已经被释放");
        }
    }

At this point, is it okay to rewrite the trylock method of the lock interface, the lock method and the unlock method?

Leave it for everyone to test and welcome to leave a message in the comment area

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324156673&siteId=291194637