[Java] manual simulation to realize an AQS

  • What is AQS?

synchronized (based on the underlying C + +, the synchronization mechanism of language implementation)

Aqs synchronizer (Java implementation)

 

  • [Unsafe] magic

Bypass the virtual machine and directly operate the underlying memory

 

  • Without saying much, we manually simulate an AQS:

1> Lock object:

package com.example.demo.thread.current;

import com.example.demo.util.UnsafeInstance;
import sun.misc.Unsafe;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.LockSupport;

/**
 * Fair lock
 *
 * @author Code Farmer
 * @date 2020/5/28 22:47
 */
public class AqsLock {

    /**
     * Current locking status, record locking times
     */
    private volatile int state = 0;

    /**
     * Thread currently holding lock
     */
    private Thread lockHolder;

    private ConcurrentLinkedQueue<Thread> waiters = new ConcurrentLinkedQueue<>();//be based on CAS Ensure the safety of entry and exit

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public Thread getLockHolder() {
        return lockHolder;
    }

    public void setLockHolder(Thread lockHolder) {
        this.lockHolder = lockHolder;
    }

    /**
     * Attempt to acquire lock
     *
     * @return
     */
    private boolean acquire() {
        Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {//Synchronizer is not locked
//            if (waiters.size() == 0 || compareAndSwapInt(0, 1)) { //If this is done, it may cause one thread to acquire the lock, and then the queue will be empty after the poll is issued, so that the next thread can acquire the lock directly
            if ((waiters.size() == 0 || current == waiters.peek()) && compareAndSwapInt(0, 1)) {
                this.setLockHolder(current);
                return true;
            }
        }
        return false;
    }

    /**
     * Lock up
     */
    public void lock() {
        if (acquire()) {//Lock successfully
            return;
        } else {
            Thread current = Thread.currentThread();
            waiters.add(current);//Save reference to thread
            for (; ; ) {
                //Get out of the way cpu Right to use
                //Thread.yield();//Will continue to waste CPU
                //Thread.sleep(1000)  //Why not use sleep?Although using sleep Will decrease cpu But 1000 ms Time could have happened 200 ms When the last thread releases the lock, other threads will block 800 ms,If it is a user business, the user experience will be very poor
                //Thread.sleep(1); //1ms It will cause frequent context switching cpu waste
                if ((current == waiters.peek()) && acquire()) {
                    waiters.poll();//After waking up, remove yourself from the queue
                    return;
                }
                //Block current thread,release cpu resources
                LockSupport.park(current);
            }
        }
    }

    /**
     * Unlock
     */
    public void unlock() {
        if (Thread.currentThread() != lockHolder) {
            throw new RuntimeException("lock holder is not current thread");
        }
        int state = getState();
        if (compareAndSwapInt(state, 0)) {
            setLockHolder(null);
            Thread first = waiters.peek();
            if (first != null) {
                LockSupport.unpark(first);//Since the wake-up thread is a random wake-up, we can't guarantee which thread wakes up, so we need to judge whether it is equal to the first thread of the queue when obtaining the lock
            }
        }
    }

    public final boolean compareAndSwapInt(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

    private static final Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();

    private static long stateOffset;

    /**
     * Calculate state variable offset
     */
    static {
        try {
            stateOffset = unsafe.objectFieldOffset(AqsLock.class.getDeclaredField("state"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

}

 

 

2. Unsafe object:

package com.example.demo.util;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @author Code Farmer
 * @date 2020/5/28 23:42
 */
public class UnsafeInstance {

    public static Unsafe reflectGetUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}

 

3> Lock in business:

package com.example.demo.service.impl;

import com.example.demo.service.AqsDemoService;
import com.example.demo.thread.current.AqsLock;

/**
 * @author Code Farmer
 * @date 2020/5/28 22:54
 */
public class AqsDemoServiceImpl implements AqsDemoService {

    AqsLock aqsLock = new AqsLock();

    @Override
    public String decStockNoLock() {

        aqsLock.lock();

        //A query operation (omitted)

        //An update operation (omitted)

        aqsLock.unlock();


        return null;

    }
}

 

 

 

  • Here, we use the [park] and [unpark] in [Unsafe] to block and wake up
#It will clear all the runtime data in the cpu cache and save it to the memory (RSS)

LockSupport.park(current);

 

#Wake up thread

LockSupport.unpark(current);

 

 

 

  • Illustrate the process of the above code:

 

 

 

 

 

 

 

 

 

 

 

 

Tags: Java

Posted on Mon, 01 Jun 2020 07:45:35 -0700 by arie_parie