JAVA多线程与高并发(四)[LockSupport,AQS解读]

link-JAVA多线程与高并发系列[前言,大纲,目录]

LockSupport

关键方法:

  1. park(): 当前线程阻塞(如果当前线程没有被unpark)
  2. unpark(Thread thread):如果入参的线程正在park(),则让它恢复运行;否则,就保证下一次该线程park()时不会阻塞.
    unpark(t)必须在线程t启动(start)后才有效果.

可以这么理解:unpark相当于是一张通行证,park是一道关卡;先拿到通行证了,遇到关卡也不会被阻拦;没有通行证时遇到关卡会被阻拦,直到有通行证了才能继续走.

public class T13_TestLockSupport {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(i + "|" + Instant.now());
                if(i == 5) {
                    LockSupport.park();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.start();

        // 下面相当于提前unpark了,因为线程t 5秒后才会park
        // 必须在t.start()后才有效果
//        LockSupport.unpark(t);

        // 下面是先park后unpark,因为8秒后会unpark线程t
        try {
            TimeUnit.SECONDS.sleep(8);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("after 8 senconds! | " + Instant.now());
        LockSupport.unpark(t);
    }
}

AQS

简介

AQS:AbstractQueuedSynchronizer
JAVA的几乎所有锁(除synchronized,LockSupport外)都是用AQS实现的,比如ReentrantLock,CountdownLatch,CyclicBarrier,Semaphore等;

可以说AQS的底层就是CAS+volatile state:

  1. 属性private volatile int state;同步状态
    这个state在不同的子类实现中有不同的含义,但都是通过这个state来控制"同步"的行为.这个state通过CAS来改变.
  2. AQS维护着Node的双向链表作为等待队列,这个等待队列是"CLH"(Craig, Landin, and Hagersten)队列的变体
    Node是AQS的内部类,Node里面的属性装着线程volatile Thread thread,和一个状态volatile int waitStatus.
    各个线程去争抢的过程其实就是这个CLH队列的入列和出列,这个过程的关键操作都是通过CAS来实现的

通过ReentrantLock的lock()方法,去阅读理解AQS

AbstractQueuedSynchronizer的state属性对于ReentrantLock的意义:
state表示线程持有锁的次数:
state=0表示没有线程持有锁;
state>0表示有一个线程持有锁,重入了state次.

起初我是看的JDK8的源码,但是它和JDK13上有一些细节上的区别,JDK13更好理解一些.我还记下了JDK8和JDK11在AQS实现上的区别,后来想想没啥意义,还容易记忆混乱,就干脆只记录JDK13的实现方式了.既然学习嘛,那就学新的~

JDK8和新版的JDK(以11为例)实现的细节上有些不同,整体思路是一样的:
CAS修改state和Node节点入队列时,JDK8通过unsafe获取变量的内存地址偏移量,和JDK11通过变量句柄指向变量的地址.
按我的理解,这俩效果是一样的,JDK11的效率应该更高一些
以state为例:
JDK8用到了stateOffset.(为了允许将来的优化没有用AtomicInteger),这个stateOffset可以理解为state的"指针"(地址),可以通过stateOffsetCAS改变state

long stateOffset=unsafe.objectFieldOffset(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
unsafe.compareAndSwapInt(this, stateOffset, expect, update);

JDK11用到了VarHandle,可以直接CAS改变state

VarHandle STATE=MethodHandles.lookup()
.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class);
STATE.compareAndSet(this, expect, update)

Unsafe是实现CAS的核心类,Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。Unsafe类提供了硬件级别的原子操作。

lock()的大致流程如下:

下面简称AbstractQueuedSynchronizer为AQS,AbstractOwnableSynchronizer为AOS

ReentrantLock Sync AQS LockSupport 调用acquire(1) 调用acquire(1) tryAcquire,再CAS获取一次锁 如果获取到了锁,就直接返回 如果没获取到锁,就把当前线程加入等待队列addWaiter 加入等待队列后再CAS尝试从队列拿到当前线程acquireQueued 如果CAS了几次仍拿不到当前线程,就park LockSupport被唤醒后返回 selfInterrupt,唤醒线程,也就是拿到了锁 线程lock()结束 ReentrantLock Sync AQS LockSupport
尝试阅读ReentrantLock的lock()方法

下面尝试着通过debugReentrantLock.lock()方法,去阅读源码.

  1. 首先随便写个方法,在lock.lock()处打断点,然后debug运行,追踪下去
	public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        System.out.println("hahaha");
        lock.unlock();
    }
  1. 看到ReentrantLock的lock()方法
    ReentrantLock.lock()方法注释的大致意思:
    这个方法用于获得该锁;
    如果该锁没有被线程持有,那么把锁的hold count设为1后直接返回;
    如果当前线程已经持有该锁,则hold count加一,然后返回;
    如果该锁被其他线程持有,那么当前线程将"阻塞",直到获得锁,然后把hold count设为1后返回
    这个lock hold count应该就是AQS的state
    /**
     * Acquires the lock.
     *
     * <p>Acquires the lock if it is not held by another thread and returns
     * immediately, setting the lock hold count to one.
     *
     * <p>If the current thread already holds the lock then the hold
     * count is incremented by one and the method returns immediately.
     *
     * <p>If the lock is held by another thread then the
     * current thread becomes disabled for thread scheduling
     * purposes and lies dormant until the lock has been acquired,
     * at which time the lock hold count is set to one.
     */
    public void lock() {
        sync.acquire(1);
    }
  1. 看到ReentrantLock.lock()其实是调用了sync.acquire(1),sync变量是Sync类型.Sync是何许人也?
    Sync,NonfairSync,FairSync都是ReentrantLock的内部类
    Sync 继承了 AbstractQueuedSynchronizer
    NonfairSync(非公平锁的实现) 和 FairSync(公平锁的实现) 都继承了 Sync
    Sync的acquire方法继承自AQS,自己没有实现:
    /**
     * Acquires in exclusive mode, ignoring interrupts.  Implemented
     * by invoking at least once {@link #tryAcquire},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquire} until success.  This method can be used
     * to implement method {@link Lock#lock}.
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

3.1 AQS的acquire中的tryAcquire是由子类实现的,
这个tryAcquire是NonfairSync实现的,调用了Sync的nonfairTryAcquire方法,因为ReentrantLock是默认是非公平锁

下面代码

/**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        @ReservedStackAccess
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            /*
            getState()调用了AQS的方法,直接返回了state属性.
			如果state等于0,则CAS设置stateOffset为acquires;
			如果state!=0,则判断拥有锁的线程是不是当前线程,如果不是则返回false;如果是当前线程已经持有锁,则更新state值为state+acquire(可重入)
			可见ReentrantLock把state作为是否锁定的判断标准,如果state=0则表示没有线程持有锁.如果state值>0,则表示已有线程持有了锁,并且持有了state次(可重入).
			*/
            int c = getState();
            if (c == 0) {
            	// compareAndSetState是AQS的方法,CAS修改state值
                if (compareAndSetState(0, acquires)) {
                	//AQS继承了AbstractOwnableSynchronizer
                	//设置状态后,则进入AbstractOwnableSynchronizer的setExclusiveOwnerThread方法.表示已经拿到锁.
                    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;
        }

3.2 如果tryAcquire没有成功获得锁,则把当前线程加入等待队列

    /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    private Node addWaiter(Node mode) {
        Node node = new Node(mode);
		// 自旋把前节点加入等待队列
        for (;;) {
            Node oldTail = tail;
            if (oldTail != null) {
                node.setPrevRelaxed(oldTail);
                if (compareAndSetTail(oldTail, node)) {
                    oldTail.next = node;
                    return node;
                }
            } else {
                initializeSyncQueue();
            }
        }
    }

然后调用acquireQueued方法

    /**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean interrupted = false;
        try {
            for (;;) {
                final Node p = node.predecessor();
                // 如果上一个节点是头节点,则说明下一个就该当前线程了
                // 那就试着去拿一下锁,tryAcquire仍是调用的子类(Sync)的方法
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                // 判断是否需要park,需要的话就调用LockSupport的park阻塞线程
                if (shouldParkAfterFailedAcquire(p, node))
                    interrupted |= parkAndCheckInterrupt();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            if (interrupted)
                selfInterrupt();
            throw t;
        }
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值