AbstractQueuedSynchronizer原理

简介

  • AbstractQueuedSynchronizer是多线程编程中程序同步的核心,所以值得深究原理。以下以ReentrantLockNonfairSync演示程序执行流程。IDEA中默认不能debugJDK的源码。可用以下设置。

    idea设置debug不跳过包

  • cas算法:compare and set,在指定内存地址中更新值时,需要对比该地址中的当前值是和期望值一致,才能更新成功。

  • NonfairSync类图

    NonfairSync类图

加锁流程

AbstractQueuedSynchronizer内部维护一个链表。head是获取锁的节点,第二节点是等待获取锁,然后想要获取锁就插入链表尾部等待。
addWaiter方法循环不停的在链表尾部插入等待者。
acquireQueued方法循环不停的去除链表首部,如果首部释放锁,立即将首部去除,第二节点获取锁,成功成为首部。如果没有释放锁,则各等待者进入wait状态。

package com.redoor.lock;

import java.util.concurrent.locks.ReentrantLock;

public class Main {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        for (int i = 0; i < 3; i++) {
            new Thread(new SubThread(lock, i)).start();
        }
    }
}
package com.redoor.lock;

import java.util.concurrent.locks.ReentrantLock;

public class SubThread implements Runnable {

    private ReentrantLock lock;
    private int i;

    public SubThread(ReentrantLock lock, int i) {
        this.i = i;
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            lock.lock();
            i++;
            System.out.println(Thread.currentThread().getName() + ":" + i);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

lock.lock()调用NonfairSync中的lock()

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

cas设置AbstractQueuedSynchronizerstatue为1,标记为加锁状态。并设置加锁的线程为当前线程。cas失败后调用AbstractQueuedSynchronizeacquire()

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

tryAcquire会调用nonfairTryAcquire 方法,如果加锁就返回false。重点看addWaiteracquireQueued

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    //如果链表tail已经初始化,则直接将等待者插入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;
        //初始化链表head、tail
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            //循环等待者插入链表tail
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            //获取node的head
            final Node p = node.predecessor();
            //如果node为等待者,链表中为第二位置,则试着获取锁
            //如果获取锁,则将链表中head去除,自己为head
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //获取锁失败,设置waitstate=-1,并将当前线程处于wait状态
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //当前线程被wake后,继续循环,只到获取锁
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

释放流程

先将锁状态去除,然后唤醒第二节点争取锁。

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;
}
public final boolean release(int arg) {
    //去除锁状态
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}


private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        //等待状态为正常
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        //唤醒第二节点,然后第二节点去除head
        LockSupport.unpark(s.thread);
}

自定义锁

AbstractQueuedSynchronizer中定义获取锁和释放锁的模版方法。acquire方法会调用tryAcquire方法,而类中该方法为空方法,方便子类重写获取锁和释放锁的条件。release方法和tryRelease方法也是一样。而对于链表的维护则不用操心。上文中的NonfairSync就是重写了这两个方法。
自定义锁,是实现Lock接口的实现类。类中定义AbstractQueuedSynchronizer的子类,并重写tryAcquiretryRelease方法。

private class Sync extends AbstractQueuedSynchronizer{

    @Override
    protected boolean tryAcquire(int arg) {
        Thread thread = Thread.currentThread();
        int state = getState();
        if (state == 0) {
            compareAndSetState(0, arg);
            setExclusiveOwnerThread(thread);
            return true;
        } else if (getExclusiveOwnerThread() == thread) {
            compareAndSetState(state, state + arg);
            return true;
        }
        return false;
    }

    @Override
    protected boolean tryRelease(int arg) {
        Thread thread = Thread.currentThread();
        if (getExclusiveOwnerThread() != thread) {
            throw new RuntimeException();
        }

        boolean flag = false;

        int state = getState() - 1;
        if (state == 0) {
            flag = true;
            setExclusiveOwnerThread(null);
        }
        setState(state);
        return flag;
    }
}
public class LucyLock implements Lock {
    private Sync sync = new Sync();

    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

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

测试类

public class Main {

    private int value;
    private LucyLock lock = new LucyLock();

    private void next(){
        try {
            lock.lock();
            Thread.sleep(300);
            value++;
            //up();//测试重入锁

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    private void up() {
        try {
            lock.lock();
            System.out.println("up");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Main main = new Main();
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                while (true) {
                    main.next();
                    System.out.println(Thread.currentThread().getName() + ":" + main.value);
                }
            }).start();
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容