分析一下CSDN大佬写的CAS,可重入锁, 非公平锁

代码来源:https://blog.csdn.net/kkgbn/article/details/100136988

对其代码添加入自己理解的注释

public class MyReentrantLock {
    
    
	// 用于执行低级、不安全操作的方法的集合
	private static final Unsafe unsafe = getUnsafe();
	// 锁柄
	private Thread lockHolder;
	// 此队列对元素进行 FIFO(先进先出)排序
	private ConcurrentLinkedQueue<Thread> queue = new ConcurrentLinkedQueue<>();

	/**
	 * 同步状态。
	 */
	private volatile int state;
	private static final long stateOffset;
	static {
    
    
		try {
    
    
			// 对象字段偏移
			stateOffset = unsafe.objectFieldOffset(MyReentrantLock.class.getDeclaredField("state"));
		} catch (Exception ex) {
    
    
			throw new Error(ex);
		}
	}

	protected final int getState() {
    
    
		return state;
	}

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

	/**
	 * 比较并设置状态
	 * @param expect
	 * @param update
	 * @return 如果成功则为true
	 */
	protected final boolean compareAndSetState(int expect, int update) {
    
    
		return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
	}

	public Thread getLockHolder() {
    
    
		return lockHolder;
	}

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

	// CAS 以及 可重入逻辑
	public boolean tryAcquire() {
    
    
		// 返回对当前正在执行的线程对象的引用。
		final Thread current = Thread.currentThread();
		// 获取状态
		int c = getState();
		if (c == 0) {
    
    
			// 比较并设置状态
			if (compareAndSetState(0, 1)) {
    
    
				// 传入当前执行的线程对象
				setLockHolder(current);
				return true;
			}
		} 
		// reentrant (可重入的), current == getLockHolder() 表示同一个对象
		else if (current == getLockHolder()) {
    
    
            int nextc = c + 1;
            if (nextc < 0) // overflow (状态 + 1 小于 零 为 溢出)
				throw new Error("Maximum lock count exceeded");
			// 设置加一后的状态值
            setState(nextc);
            return true;
        }
		return false;
	}

	public boolean acquire() {
    
    
		// try acquire
		if (tryAcquire()) {
    
    
			return true;
		}
		// add queue and spin (添加队列和旋转)
		final Thread current = Thread.currentThread();
		// 如果状态不为0,线程对象不是一个,则加入队列
		queue.add(current);
		for (;;) {
    
    
			// peek :检索但不删除此队列的头部,如果此队列为空,则返回null 。
			if (current == queue.peek() && tryAcquire()) {
    
    // TODO CAS
				// 检索并删除此队列的头部,如果此队列为空,则返回null 。
				queue.poll();
				return true;
			}
			// 用于创建锁和其他同步类的基本线程阻塞原语。
			LockSupport.park();
		}
	}

	/**
	 * 加锁
	 */
	public void lock() {
    
    
		// 比较设置状态
		if (compareAndSetState(0, 1)) {
    
    
			// 设置成功 , 设置 当前正在执行的线程对象的引用。
			setLockHolder(Thread.currentThread());
			return;
		}
		// 设置失败 , 尝试获取
		acquire();
	}

	/**
	 * 解锁
	 */
	public void unlock() {
    
    
		Thread current = Thread.currentThread();
		if (current != lockHolder) {
    
    
			throw new IllegalMonitorStateException("can't unlock");
		}
		int c = getState() - 1;
		if (c == 0) {
    
    
			// 状态为0 , 表示解锁, LockHolder 值为空
			setLockHolder(null);
		}
		setState(c);
		Thread waiter = queue.peek();
		// 不为空,表明队列中还有线程
		if (null != waiter) {
    
    
			// 解除阻塞
			LockSupport.unpark(waiter);
		}
	}
	// 不安全的工具
	public static Unsafe getUnsafe() {
    
    
		Unsafe unsafe = null;
		try {
    
    
			Constructor<Unsafe> constructor = Unsafe.class.getDeclaredConstructor(new Class<?>[0]);
			constructor.setAccessible(true);
			unsafe = constructor.newInstance(new Object[0]);
		} catch (Exception e) {
    
    
			e.printStackTrace();
		}
		return unsafe;
	}
}

编写测试类

@Slf4j
public class MyLockTest {
    
    
    public static void main(String[] args) {
    
    

        MyReentrantLock myReentrantLock = new MyReentrantLock();

        new Thread(() -> {
    
    
            log.debug("{}:开始加锁", Thread.currentThread().getName());
            myReentrantLock.lock();
            log.debug("{}:加锁完毕,状态为:{}",Thread.currentThread().getName(), myReentrantLock.getState());
            // TimeUnit.SECONDS.sleep(2);
            myReentrantLock.unlock();
            log.debug("{}:解锁完毕", Thread.currentThread().getName());
            log.debug("{}:解锁完毕,状态为:{}",Thread.currentThread().getName(), myReentrantLock.getState());

        }, "t").start();

        new Thread(() -> {
    
    
            log.debug("{}:开始加锁", Thread.currentThread().getName());
            myReentrantLock.lock();
            log.debug("{}:加锁完毕,状态为:{}",Thread.currentThread().getName(), myReentrantLock.getState());
            // TimeUnit.SECONDS.sleep(2);
            myReentrantLock.unlock();
            log.debug("{}:解锁完毕", Thread.currentThread().getName());
            log.debug("{}:解锁完毕,状态为:{}",Thread.currentThread().getName(), myReentrantLock.getState());

        }, "t1").start();
    }
}

输出结果

21:43:39.108 [t1] DEBUG com.xiaozheng.cas.MyLockTest - t1:开始加锁
21:43:39.108 [t] DEBUG com.xiaozheng.cas.MyLockTest - t:开始加锁
21:44:08.973 [t1] DEBUG com.xiaozheng.cas.MyLockTest - t1:加锁完毕,状态为:1
21:44:08.973 [t1] DEBUG com.xiaozheng.cas.MyLockTest - t1:解锁完毕
21:44:08.973 [t1] DEBUG com.xiaozheng.cas.MyLockTest - t1:解锁完毕,状态为:0
21:44:09.937 [t] DEBUG com.xiaozheng.cas.MyLockTest - t:加锁完毕,状态为:1
21:44:09.937 [t] DEBUG com.xiaozheng.cas.MyLockTest - t:解锁完毕
21:44:09.937 [t] DEBUG com.xiaozheng.cas.MyLockTest - t:解锁完毕,状态为:0

自己的一点点总结

  • 主要是判断状态和线程对象是否是同一个对象,来进行是否加锁
  • 如果状态不为0线程对象不是同个,表示有线程要竞争锁,则将其线程加入队列,并暂时park该线程
  • 等加锁线程执行完毕进行解锁时,会将park的线程唤起,此时竞争该锁的线程会重新进入下轮的队列遍历,拿到当前线程进行状态设置和设置lockHolder为当前线程
  • 解锁时,状态减1,为0时,设置lockHoldernull,等待下个加锁线程赋值,队列不为空进行唤醒park

猜你喜欢

转载自blog.csdn.net/u013494827/article/details/126004254