# 前言

实现自定义锁需要考虑的事情:

1. 锁的是线程,争用,首先有一个变量,用于表示锁与未锁的状态,还需要标识出是哪个线程获得了锁,其它线程等待的队列

2. 线程阻塞,精确唤醒指定线程,使用LockSupport

3. 原子性操作状态CAS

4. 线程排队,使用队列(链路)

# 一、JUC中Lock

juc中的Lock定义方法如下:

一闪灵光手写教程(手写系列-Lock)(1)

具体的实现有:

一闪灵光手写教程(手写系列-Lock)(2)

以ReentrantLock为例,默认使用的是非公平锁进行加锁

static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else //已经加锁失败,为何还要继续尝试加锁? //A:线程在park后恢复,需要继续尝试获取锁 acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }

# 二、自定义Lock实现

仿照上面的说明,实现了一把简单的锁,比不上JUC中的实现,仅供进行原理学习

package com.zte.sunquan.demo.lock; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.locks.LockSupport; import lombok.Data; import sun.misc.Unsafe; @Data public class SQLock { //private AtomicInteger state=new AtomicInteger(1); private static final Unsafe unsafe = UnsafeInstance.getInstance(); private static final long stateOffset; static { try { stateOffset = unsafe.objectFieldOffset(SQLock.class.getDeclaredField("state")); } catch (Exception e) { throw new Error(e); } } /** * 锁状态 */ private volatile int state = 0; /** * 当前获得锁的线程 */ private Thread lockHolder; /** * 线程缓存队列 * * @return */ private Queue<Thread> queue = new ConcurrentLinkedDeque<>(); /** * 尝试加锁 * * @return */ private boolean tryAcquire() { int state = getState(); Thread thread = Thread.currentThread(); if (state == 0) { //比较两种写法的不同 //queue.add不应该写,应该增加线程到队列只在Lock里进行 //先排队-第一个则可以买票 // if (!queue.isEmpty() && queue.peek() != thread) { // queue.add(thread); // } else { // if (compareAndSwapState(state, 1)) { // //记录当前线程拿到锁 // setLockHolder(thread); // return true; // } // } if ((queue.isEmpty() || thread == queue.peek()) && compareAndSwapState(0, 1)) { setLockHolder(thread); return true; } } return false; } /** * 加锁 */ public void lock() { if (tryAcquire()) { System.out.println("lock Thread-name:" Thread.currentThread().getName()); return; } Thread thread = Thread.currentThread(); queue.add(thread);//何时移除? //未拿到锁的线程阻塞(或自旋-占CPU for(;;)) 为何不直接阻塞? for (; ; ) { if (queue.peek() == thread && tryAcquire()) {//直接去拿,非公平,先排队再买票 System.out.println("Thread-name:" thread.getName()); queue.poll(); return; } //解锁后需要唤醒,唤醒后立即再次尝试获得锁 LockSupport.park(thread); } } /** * 解锁 */ public void unLock() { //只能当前持久锁的线程解锁,其它线程无权解锁 Thread thread = Thread.currentThread(); if (thread != lockHolder) { throw new RuntimeException("Can not unlock."); } int state = getState(); if (compareAndSwapState(state, 0)) { System.out.println(String.format("unlock success:%s", thread.getName())); setLockHolder(null);//无线程持有锁 Thread head = queue.peek(); if (head != null) { LockSupport.unpark(head); } } } private boolean compareAndSwapState(int oldVal, int newVal) { return unsafe.compareAndSwapInt(this, stateOffset, oldVal, newVal); } public void printQueue() { System.out.println(this.queue.size()); } }

package com.zte.sunquan.demo.lock; import java.lang.reflect.Field; import sun.misc.Unsafe; public class UnsafeInstance { private UnsafeInstance() { } public static Unsafe getInstance() { return Singleton.unsafe; } private static class Singleton { private static Unsafe unsafe; static { try { Field getUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); getUnsafe.setAccessible(true); unsafe = (Unsafe) getUnsafe.get(null); } catch (Exception e) { e.printStackTrace(); } } } }

测试用例:

package com.zte.sunquan.demo.lock; import java.util.concurrent.CountDownLatch; public class SQLockTest { private static int count = 0; public static void main(String[] args) throws InterruptedException { SQLock lock=new SQLock(); CountDownLatch latch = new CountDownLatch(100); for (int x=0;x<100;x ){ new Thread(() -> { try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } //System.out.println("start:" Thread.currentThread().getName()); lock.lock(); for (int i = 0; i < 100; i ) { count ; } //System.out.println("bbb:" Thread.currentThread().getName()); //lock.unLock(); }).start(); latch.countDown(); } Thread.sleep(500); System.out.println("total:" count); lock.printQueue(); } }

,