【多线程】锁的实现机制及AQS抽象

一、自己先实现一个锁

思路:使用CAS原子操作实现线程安全+使用阻塞队列实现排队等待
上代码:

public class MyLock implements Lock {
	volatile AtomicReference<Thread> flag = new AtomicReference<Thread>();// 锁标志位
	volatile LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<Thread>();// 存放等待线程

	@Override
	public boolean tryLock() {
		// 使用CAS原子操作加锁
		return flag.compareAndSet(null, Thread.currentThread());
	}

	@Override
	public void lock() {
		boolean in = true;// 放置多次入队
		while (!tryLock()) {
			if (in) {// 没拿到锁则进入队列等待
				waiters.offer(Thread.currentThread());
				in = false;
				LockSupport.park();// 当前线程挂起,阻塞执行
			}
		}
		waiters.remove(Thread.currentThread());// 拿到锁则从等待队列中移除
	}

	@Override
	public void unlock() {
		if (flag.compareAndSet(Thread.currentThread(), null)) {// 重置标志位为null
			Iterator<Thread> it = waiters.iterator();
			while (it.hasNext()) {
				LockSupport.unpark(it.next());
			}
		}
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		// TODO Auto-generated method stub

	}

	@Override
	public Condition newCondition() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		// TODO Auto-generated method stub
		return false;
	}

}

test:

public class LockTest4 {
	private static final Lock lock = new MyLock();

	public static void main(String[] args) {
		LockTest4 lt = new LockTest4();
		new Thread(new Runnable() {
			@Override
			public void run() {
				lt.mm();
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				lt.mm();
			}
		}).start();
	}

	private static void mm() {
		lock.lock();
		try {
			System.out.println(Thread.currentThread().getName() + "开始执行...");
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "执行结束...");
		} finally {
			lock.unlock();
		}
	}
}

输出结果:
Thread-0开始执行…
Thread-0执行结束…
Thread-1开始执行…
Thread-1执行结束…

二、jdk中的锁实现

我们进入到ReentrantLock的几个方法实现:

	public void lock() {
        sync.lock();
	}
 	public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
	}
	public void unlock() {
        sync.release(1);
    }
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

看到这里,是否会惊讶到,这么简洁么!但sync是个什么鬼?
我们继续跟踪:

 /** Synchronizer providing all implementation mechanics */
 //同步器提供所有实施机制
    private final Sync sync;
    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {

这里翻译过来就是ReentrantLock内部实现了一个静态内部类-同步控制器实现类,而它是继承于最高级封装的AbstractQueuedSynchronizer抽象队列同步器实现的,且后续有公平和非公平两个版本(NonfairSync ,FairSync),AQS state用于持有锁计数,这个是指重入锁。
ReentrantLock支持公平锁与非公平锁,默认是非公平锁

	public ReentrantLock() {
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
  • 公平锁
		final void lock() {
            acquire(1);
        }

这里调用了AQS的阻塞式获取锁资源

 public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
  • 非公平锁
	final void lock() {
            if (compareAndSetState(0, 1))//首先就会直接尝试CAS获取锁,插队
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

三、AQS封装及使用

1、两种方式

独享操作共享操作描述
acquireacquireShared获取锁资源,没拿到则等待,由AQS实现
releasereleaseShared释放所资源,由AQS实现
tryAcquiretryAcquireShared执行占用资源的操作,由使用者实现
tryReleasetryReleaseShared执行释放资源的操作,由使用者实现

2、一个标志位

/**
     * The synchronization state.
     */
    private volatile int state;

这个state状态作用有三:

  • 标识锁是否被持有,默认为0
	final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
  • 用于重入锁计数,每lock一次就会+1,相对释放锁就是恢复的过程
	protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
  • 用于Semaphore信号量限流

3、一个链表

AQS中自己维护了一个双向链表结构来实现线程队列等待。

static final class Node {
		//表示共享模式(共享锁)
        static final Node SHARED = new Node();
        //表示独占模式(独占锁)
        static final Node EXCLUSIVE = null;
         //表示线程已取消
        static final int CANCELLED =  1;
        //表示当前结点的后继节点需要被唤醒
        static final int SIGNAL    = -1;
        //线程(处在Condition休眠状态)在等待Condition唤醒
        static final int CONDITION = -2;
        /表示锁的下一次获取可以无条件传播,在共享模式头结点有可能处于这种状态
        static final int PROPAGATE = -3;
        //线程等待状态
        volatile int waitStatus;
        //前继节点
        volatile Node prev;
        //后继节点
        volatile Node next;
        //当前节点所代表的的线程
        volatile Thread thread;
        //可以理解为当前是独占模式还是共享模式
        Node nextWaiter;
        //如果节点在共享模式下等待,则返回true
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
        //获取前一个节点
       final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }
        //三个构造方法分别用于不同场景
        Node() {   
        }
        Node(Thread thread, Node mode) { 
            this.nextWaiter = mode;
            this.thread = thread;
        }
        Node(Thread thread, int waitStatus) {
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }
  • 入队
    如果当前线程通过CAS获取锁失败,则会将该线程信息封装为一个节点,追加到队列末尾。
	//获取锁资源
	public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    //包装当前线程,添加到队列中		
	private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            //快速的将节点插入队列尾部
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //快速插入失败,通过轮询来插入尾部,性能比快速插入消耗要大一些
        enq(node);
        return node;
    }
	private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
	//挂起当前线程
	final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //通过park/unpark机制
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
  • 出队
	//释放锁资源
	public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
            	//唤醒并出队
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

4、自己实现一个AQS

基于我们开始实现的锁,我们也抽象封装成自己的简单AQS同步器来使用:
这里只实现了独享锁,偷懒使用了现成的队列…

public class MyAQS {
	public volatile AtomicReference<Thread> flag = new AtomicReference<Thread>();// 锁标志位
	volatile LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<Thread>();// 存放等待线程

	public boolean tryAcquire() {// 需要使用者自己实现
		throw new UnsupportedOperationException();
	}

	public void acquire() {
		boolean in = true;// 放置多次入队
		while (!tryAcquire()) {
			if (in) {// 没拿到锁则进入队列等待
				waiters.offer(Thread.currentThread());
				in = false;
				LockSupport.park();// 当前线程挂起,阻塞执行
			}
		}
		waiters.remove(Thread.currentThread());// 拿到锁则从等待队列中移除
	}

	public boolean tryRelease() {
		throw new UnsupportedOperationException();
	}

	public void release() {// 释放锁资源操作交给子类,这里定义了释放资源后的操作
		if (tryRelease()) {
			Iterator<Thread> it = waiters.iterator();
			while (it.hasNext()) {
				LockSupport.unpark(it.next());
			}
		}
	}
}
public class MyLock implements Lock {

	MyAQS aqs = new MyAQS() {
		@Override
		public boolean tryAcquire() {
			return flag.compareAndSet(null, Thread.currentThread());
		}

		@Override
		public boolean tryRelease() {
			return flag.compareAndSet(Thread.currentThread(), null);
		}

	};

	@Override
	public boolean tryLock() {
		// 使用CAS原子操作加锁
		return aqs.tryAcquire();
	}

	@Override
	public void lock() {
		aqs.acquire();
	}

	@Override
	public void unlock() {
		aqs.release();
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		// TODO Auto-generated method stub

	}

	@Override
	public Condition newCondition() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		// TODO Auto-generated method stub
		return false;
	}

}

5、

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值