深入理解并发编程-ReentranLock

本文为读书笔记,书籍为Java并发编程的艺术

1.公平锁

公平性与否是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。

使用公平锁时,加锁方法lock()调用轨迹如下。
1)ReentrantLock:lock()。
2)FairSync:lock()。
3)AbstractQueuedSynchronizer:acquire(int arg)。
4)ReentrantLock:tryAcquire(int acquires)。
在第4步真正开始加锁,下面是该方法的源代码。

//java/util/concurrent/locks/AbstractQueuedSynchronizer.java
  public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//java.util.concurrent.locks.ReentrantLock.FairSync
  protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //  private volatile int state;  在 AbstractQueuedSynchronizer
            int c = getState();//获取锁的开始,首先读volatile变量state
            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;
        }

解锁:
1)ReentrantLock:unlock()。
2)AbstractQueuedSynchronizer:release(int arg)。
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);
            return free;
        }

非公平锁

只要在自旋时设置同步状态成功就代表获取到了锁
加锁方法lock()调用轨迹如下。
1)ReentrantLock:lock()。
2)NonfairSync:lock()。
3)AbstractQueuedSynchronizer:compareAndSetState(int expect, int update)。

   final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            //获取不到锁
                acquire(1);
        }
//AbstractQueuedSynchronizer:compareAndSetState
  protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

解锁同公平锁

3. 获取不到锁的状态

  static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            m1();
        }, "t1").start();

        Thread.sleep(10);
		//观察t2
        new Thread(() -> {
            m2();
        }, "t2").start();

    }

    public static void m1() {
        reentrantLock.lock();
        try {
            try {
                //睡眠
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            reentrantLock.unlock();
        }
    }

    public static void m2() {
        System.out.println("尝试获取锁");
        reentrantLock.lock();
        try {
            System.out.println("获取到锁");
        } finally {
            reentrantLock.unlock();
        }
    }

在这里插入图片描述
我们知道synchronized获取不到锁是阻塞状态的,这两个锁的状态不一样哦
至于原因就得看源码了

  public final void acquire(int arg) {
  // 再次尝试获取状态,如果还是获取不到就把 当前线程加入队列 
  //返回值是线程是否在自旋的过程中被挂起(park),如果被挂起了,就唤醒
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();//  Thread.currentThread().interrupt();
    }
 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;
                    //退出自旋,放回线程是否被挂起 (park)
                    return interrupted;
                }
                //以上的条件都没有满足,就把当前线程park
              	
              	// 检查和更新未能获取的节点的状态。
				//如果线程阻塞,返回true。这是主信号
				//控制所有获取循环。需要pred == node.prev。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
        //结束的时候,如果还是获取不到状态,那就取消这个节点了
            if (failed)
            //取消正在进行的获取尝试。
                cancelAcquire(node);
        }
    }
  private final boolean parkAndCheckInterrupt() {
 		 // 挂起挂起
        LockSupport.park(this);
        //返回当前线程状态 true
        return Thread.interrupted();
    }

所以线程处于waitting状态,就是在自旋的过程中线程被阻塞了;
即 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())这一段关键代码

4.总结

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

锁释放-获取的内存语义的实现至少有下面两种方式:
1)利用volatile变量的写-读所具有的内存语义。
2)利用CAS所附带的volatile读和volatile写的内存语义。

发布了37 篇原创文章 · 获赞 6 · 访问量 4635

猜你喜欢

转载自blog.csdn.net/littlewhitevg/article/details/105584195