重学多线程(三)—— 锁

前言

锁是用来控制多个线程访问共享资源的方式。在Lock接口出现之前,Java程序是靠synchronized关键字实现锁功能,虽然Lock在使用时多了显式地获取和释放,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized关键字不具备的同步特性。

Lock的API接口

//Lock是一个接口,接口,接口!重要的事情说三遍。
public interface Lock {
    //获取锁
    void lock();

    //可中断地获取锁
    void lockInterruptibly() throws InterruptedException;

    //尝试非阻塞获取锁,立刻返回
    boolean tryLock();

    //超时获取锁
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    //释放锁
    void unlock();

    //获取等待通知组件
    Condition newCondition();
}

Lock的内部实现

AQS队列同步器介绍

AQS(AbstractQueuedSynchronizer)队列同步器,是用来构建锁或者其他同步组件的基础框架。一般通过继承同步器并实现它的抽象方法的方式来管理同步状态。同步器提供了3个方法(getState(),setState(int newState)和compareAndSetState(int expect, int update))来更改同步状态。
同步器是实现锁的关键,锁在实现过程中聚合同步器,利用同步器实现锁的语义。

同步器提供了如下可重写的方法:

protected boolean tryAcquire(int var1) {
        throw new UnsupportedOperationException();
    }

    protected boolean tryRelease(int var1) {
        throw new UnsupportedOperationException();
    }

    protected int tryAcquireShared(int var1) {
        throw new UnsupportedOperationException();
    }

    protected boolean tryReleaseShared(int var1) {
        throw new UnsupportedOperationException();
    }

    protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }

AQS队列同步器实现

同步队列

同步器依赖内部的同步队列来完成同步状态的管理,队列节点数据结构如下:

static final class Node {
        static final AbstractQueuedSynchronizer.Node SHARED = new AbstractQueuedSynchronizer.Node();
        static final AbstractQueuedSynchronizer.Node EXCLUSIVE = null;
        static final int CANCELLED = 1;
        static final int SIGNAL = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile AbstractQueuedSynchronizer.Node prev;
        volatile AbstractQueuedSynchronizer.Node next;
        volatile Thread thread;
        AbstractQueuedSynchronizer.Node nextWaiter;

        final boolean isShared() {
            return this.nextWaiter == SHARED;
        }

        final AbstractQueuedSynchronizer.Node predecessor() throws NullPointerException {
            AbstractQueuedSynchronizer.Node var1 = this.prev;
            if (var1 == null) {
                throw new NullPointerException();
            } else {
                return var1;
            }
        }

        Node() {
        }

        Node(Thread var1, AbstractQueuedSynchronizer.Node var2) {
            this.nextWaiter = var2;
            this.thread = var1;
        }

        Node(Thread var1, int var2) {
            this.waitStatus = var2;
            this.thread = var1;
        }
    }

从上述代码可知,同步队列是一个FIFO双向队列。

独占式同步状态获取
public final void acquire(int var1) {
        if (!this.tryAcquire(var1) && this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), var1)) {
            selfInterrupt();
        }
    }
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        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;
                }
            }
        }
    }

当线程调用acquire(int)方法时,具体流程如下:
这里写图片描述

共享式同步状态获取
public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
独占式超时获取同步状态
private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

具体流程如下:
这里写图片描述

常用锁

ReentrantLock

ReentrantLock,重入锁,能够支持一个线程对资源的重复加锁。该锁支持获取锁时的公平性和非公平性选择。

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    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;
        }
protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
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;
        }

公平锁和非公平锁的唯一区别是hasQueuedPredecessors()方法,即加入了同步队列中当前节点是否有前驱节点的判断。

公平锁保证了锁的获取按照FIFO原则,而代价是进行了大量的线程切换;非公平锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了更大的吞吐量。

ReentrantReadWriteLock

ReentrantReadWriteLock,可重入读写锁,实现了ReadWriteLock接口,通过readLock()方法和writeLock()方法获取读锁和写锁。

读写状态设计

读写锁需要在同步状态上维护多个度线程和一个写线程的状态。读写锁将变量切分成了两个部分,高16位表示读,低16位表示写。假设当前读写状态为S,写状态等于S&0x0000FFFFF,读状态等于S>>>16。写状态增加1时,等于S+1,读状态增加1时,等于S+(1<<16)。

写锁获取

写锁是一个支持重入的排它锁。如果当前线程已经获取了写锁,则增加写状态;如果读锁已经被获取或者获取写锁的线程不是当前线程,则当前线程进入等待状态。

protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }
读锁获取

读锁是一个支持重入的共享锁,在没有其他写线程访问时,读锁总会被成功获取,如果当前线程已经获取读锁,则增加读状态。而当前线程在获取读锁时,写锁已被其他线程获取,则进入等待状态。

protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
锁降级

锁降级是指把持住当前拥有的写锁,再获取到读锁,随后释放写锁的过程。重点在于先获取读锁,在获取读锁的过程中保持本身的写锁,保证了数据的可见性。

Condition接口

Condition接口提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式。

public interface Condition {
    //相当于Object的wait()方法
    void await() throws InterruptedException;

    void awaitUninterruptibly();

    long awaitNanos(long nanosTimeout) throws InterruptedException;

    //相当于Object的wait(long timeout)方法
    boolean await(long time, TimeUnit unit) throws InterruptedException;

    boolean awaitUntil(Date deadline) throws InterruptedException;

    //相当于Object的notify()方法
    void signal();

    //相当于Object的notifyAll()方法
    void signalAll();
}

总结

一般而言,Lock对象能将synchronized关键字替换掉,是synchronized关键字的进阶。掌握Lock有助于学习并发包中源代码的实现原理,才能在实际开发中运用自如。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值