深入理解Java内存模型 ch5 锁

1.锁的释放、获取建立的happens before关系

深入理解Java内存模型 ch5 锁_第1张图片

2.锁释放、获取的内存语义

  • 当线程释放锁时, JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
  • 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须要从主内存中去读取共享变量。

3.锁内存语义的实现

深入理解Java内存模型 ch5 锁_第2张图片
private volatile int state;

ReentrantLock的构造函数:

   private final Sync sync;

    public ReentrantLock() {
        sync = new NonfairSync();
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

加锁ReentrantLock.lock:

    public void lock() {
        sync.lock();
    }

释放锁ReentrantLock.unlock:

    public void unlock() {
        sync.release(1);
    }

3.1 ReentrantLock公平锁

3.1.1 加锁

1.ReentrantLock.lock()
2.FairSync.lock()
3.AbstractQueuedSynchronizer.acquire(int arg)

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

4.FairSync.tryAcquire()

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();//获取锁的开始,首先读volatile变量
            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;
        }

3.1.2 释放锁

1.ReentrantLock.unlock()
2.AbstractQueuedSynchronizer.release(int arg)

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

3.Sync.tryRelease(int releases)

        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); //释放锁最后,写volatile变量state
            return free;
        }

3.1.3 state

公平锁在释放的最后写volatile变量state;在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变的对获取锁的线程可见。

3.2 ReentrantLock非公平锁

3.2.1 加锁

ReentrantLock加锁的方法调用轨迹:
1.ReentrantLock.lock()
2.NonfairSync.lock()

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

3.AbstractQueuedSynchronizer.compareAndSetState(int expect, int update)

    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

该方法以原子的方式更新state变量。

CAS同时具有volatile读和volatile写的内存语义。

3.2.2 释放锁

同公平锁一致。

3.3 总结

  • 公平锁和非公平锁释放时,最后都要写一个volatile变量state
  • 公平所获取时,会去读volatile变量
  • 非公平锁获取时,首先会用CAS更新这个volatile变量,这个操作同时具有volatile读和volatile写的内存语义。

4.concurrent包的实现

由于CAS具有volatile读和volatile写的内存语义,因此java线程之间通信有下面四种方式:

  • A线程写volatile变量,随后B读
  • A写volatile,随后B用CAS更新
  • A用CAS更新,随后B用CAS更新
  • A用CAS更新,随后B读

仔细分析concurrent包,会发现一个通用化的实现模式:

  • 首先,声明共享变量为volatile
  • 然后,使用CAS的更新实现线程之间的同步
  • 同时,配合以volatile的写/读和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。
深入理解Java内存模型 ch5 锁_第3张图片

你可能感兴趣的:(深入理解Java内存模型 ch5 锁)