Java锁机制(四)Lock

114 阅读3分钟

Lock

image.png

ReentrantLock

    public class ReentrantLockDemo {

        static Lock lock = new ReentrantLock();

        private static int count = 0;
        private static void inc(){
//        lock.lock();//抢占锁、如果没有抢占到锁,会阻塞
            if (lock.tryLock()){//如果没有抢占到锁,这里不会阻塞
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
//            lock.unlock();
            }
            count ++;
        }

        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 1000; i++) {
                new Thread(() ->{
                    ReentrantLockDemo.inc();
                }).start();
            }
            Thread.sleep(3000);
            System.out.println("result:"+count);
        }
    }

    result:982
    由于不是原子性,因此多线程相加会导致错误。

加上Lock

    lock.lock();//抢占锁、如果没有抢占到锁,会阻塞
    ... ...
    finally {lock.unlock();}

    result:1000
    由于加了锁,因此数据正常

ReentrantLock的实现原理

锁的特性:满足互斥性、意味着同一个时刻,只允许一个线程进入到加锁的代码中,在多线程环境下,可以让线程顺序访问

锁的设计猜想

  • 一定会涉及到锁的抢占,需要有一个标记来实现互斥,全局变量(0、1)
  • 抢占到了锁,怎么处理
  • 未抢占到锁,怎么处理
  1. 需要等待(让处于排队中的线程,如果没有抢占到锁,则直接先阻塞,然后释放cpu资源)

如何等待?->wait/notify(线程通信的机制,无法指定唤醒某一个线程) LockSupport.park/unpark(阻塞一个制定的线程,唤醒一个指定的线程) condition

  1. 需要排队(允许有N个线程被阻塞,此时线程处于活跃状态)
  • 抢占到锁的释放过程,怎么处理
  1. LockSupport.unpark-->唤醒处于队列中的指定线程
  • 锁抢占的公平性(是否允许插队)
  1. 公平
  2. 非公平(synchronized)

AbstractQueuedsynchronizer(AQS)

Lock核心的机制在这里面做,提供了2种机制

  • 共享锁
  • 互斥锁

image.png

公平锁

ReentrantLock:
final void lock() {
    acquire(1);//抢占一把锁
}

AbstractQueuedsynchronizer:
public final void acquire(int arg) {-->AQS里的方法
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

ReentrantLock:
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {//无锁状态
        if (!hasQueuedPredecessors() && //是否存在一个处理的队列
                compareAndSetState(0, acquires)) { //cas操作-->(lock指令,cpu加锁):只有当前不存在队列的情况下才会cas
            setExclusiveOwnerThread(current);//把获得锁的线程保存到ExclusiveOwnerThread中
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {//如果当前获得锁的线程和当前占用锁的线程是同一个,表示重入锁,不需要cas操作
        int nextc = c + acquires;//增加重入次数
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);//保存state
        return true;
    }
    return false;
}

公平锁

ReentrantLock:
final void lock() {
    if (compareAndSetState(0, 1))//不管当前AQS队列中是否有排队的情况,先去插队
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

AbstractQueuedsynchronizer:
public final void acquire(int arg) {-->AQS
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

ReentrantLock:
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

ReentrantLock:
final boolean nonfairTryAcquire(int acquires) {-->非公平
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {//不去判断hasQueuedPredecessors
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

加入队列并且进行自旋等待

如果没有抢占到锁,则加入队列进行自旋等待

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

addWaiter(Node.EXCLUSIVE)---》添加一个互斥锁的节点
acquireQueued()---》自旋锁和阻塞的操作

addWaiter()

private Node addWaiter(Node mode) {
    // 把当前线程封装成Node节点
    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()))//初始化head节点
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}