3、显示锁和AQS

3、显示锁和AQS

原子操作CAS
atom(不可分割)

什么是原子操作?如何实现原子操作?
synchronized 基于阻塞的锁机制 可以实现,但会引发以下问题

1、被阻塞的线程优先级很高

2、拿到锁的线程一直不释放锁怎么办?

3、大量的竞争,消耗CPU,同时带来死锁或者其他安全问题。

CAS的原理
利用了现代处理器都支持的CAS指令,循环这个指令,直到成功为止;

CAS(Compare And Swap),指令级别保证这是一个原子操作;

三个运算符: 一个内存地址 V 、 一个期望值 A 、 一个新值 B

基本思路:如果地址V上的值和我期望的值A相等,就给地址V赋给新值B,如果不是,不做任何操作;

循环(死循环,自旋)里面不断的进行CAS操作;

CAS的问题
ABA问题
A - 》 B - 》 A

加版本号控制

A1 - 》 B2 - 》 A3

开销问题
CAS操作长期不成功,CPU不断循环

只能保证一个共享变量的原子操作
多个变量封装成一个对象使用

jdk相关原子操作类的使用;
AtomicMarkableReference , boolean 有没有动过

AtomicStampedReference , 动过几次

显示锁
Lock 接口和 synchronized 比较
synchronized 代码简洁,

lock 获取锁可以被中断,超时获取锁,尝试获取锁

不是特别必要 尽量使用 synchronized

可重入锁 ReentrantLock 、所谓的公平锁和非公平锁
如果在时间上,先对锁获取的请求 一定先被满足,就是公平锁

不满足 则是非公平锁;

非公平锁的效率一般来讲更高;

ReentrantLock(boolean fair) 可指定是否公平;

ReadWriteLock 、 ReentrantReadWriteLock
ReentrantLock 和 synchronized 都是排他锁

读写锁:同一时刻允许多个读线程同时访问;但写线程访问的时候所有的读和写都被阻塞;

读多写少的情况 读写锁性能提升非常明显 但是什么业务场景会用到呢?

什么是AQS ? 学习它的必要性
了解LockSupport工具
作用:
阻塞一个线程
唤醒一个线程

构建同步组件的基础工具
part 开头的方法
unpark(Thread thread)

AQS使用方式和其中的设计模式
继承,模版方法设计模式

了解其中的方法
模版方法

独占式获取锁

void acquire(int arg)
void acquireInterruptibly(int arg)
boolean tryAcquire(int arg)

独占式释放锁
boolean release(int arg)

共享式获取锁
void acquireShared(int arg)
void acquireSharedInterruptibly(int arg)
boolean tryAcquireSharedNanos(int arg, long nanosTimeout)

共享式释放锁
boolean releaseShared(int arg)

需要子类覆盖的流程方法
独占式获取
boolean tryAcquire(int arg)

独占式释放
boolean tryRelease(int arg)

共享式获取
int tryAcquireShared(int arg)

共享式释放
boolean tryReleaseShared(int arg)

这个同步器是否处于独占模式
boolean isHeldExclusively()

同步状态 volatile int state
获取同步状态 final int getState()
设置同步状态 final void setState(int newState) 无法保证原子性

设置同步状态 compareAndSetState(int expect, int update) 使用CAS设置 、保证设置状态的原子性

AQS中的数据结构-节点和同步队列
AbstractQueuedSynchronizer 深入分析
先进先出

class Node
waitStatus:
CANCELLED : 线程等待超时或者中断了,需要从队列里移走;
SIGNAL : 后续的节点处于等待状态,当前节点,通知后面的节点去运行;
CONDITION: 当前节点处于等待队列;
PROPAGATE: 共享,表示状态要往后面的节点传播;
0 表示当前节点处于初始状态;

Node prev; 上一个线程
Node next; 下一个线程

独占式状态获取与释放

在这里插入图片描述
独占式获取锁 源代码解析:

public final void acquire(int arg) {
				//获取锁成功就返回,否则 加入等候队列
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }


private Node addWaiter(Node mode) {
				//当前线程包装成 Node
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        //   快速加入尾节点;加入成功return
        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;
                }
            }
        }
    }


    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
            		// 临时变量p 设置为当前节点的头节点
                final Node p = node.predecessor();
                // 如果当前节点的头节点是  head 节点 并且尝试获取锁成功
                //则 设置当前节点为头节点 。回收这一节点
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 否则   阻塞当前线程节点
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }unlock源代码解析
    }

unlock源代码解析:

public final boolean release(int arg) {
				// 尝试释放锁成功
        if (tryRelease(arg)) {
        		// 变量h  为当前头节点
            Node h = head;
            // 如果h 不为 null  并且  等候状态不为0 
            if (h != null && h.waitStatus != 0)
                //  唤醒头节点
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    

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



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)
            LockSupport.unpark(s.thread);
    }

共享式获取锁:

public final void acquireShared(int arg) {
				// 获取锁失败返回 < 0 
        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 (;;) {
            // 死循环 设置 临时变量p 为当前节点的上一节点
                final Node p = node.predecessor();
                // 如果是头节点
                if (p == head) {
                // 尝试获取锁
                    int r = tryAcquireShared(arg);
                    //获取成功
                    if (r >= 0) {
                    //设置当前节点为头节点 并往下传播 r 个节点
                        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 void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        /*
         * Try to signal next queued node if:
         *   Propagation was indicated by caller,
         *     or was recorded (as h.waitStatus either before
         *     or after setHead) by a previous operation
         *     (note: this uses sign-check of waitStatus because
         *      PROPAGATE status may transition to SIGNAL.)
         * and
         *   The next node is waiting in shared mode,
         *     or we don't know, because it appears null
         *
         * The conservatism in both of these checks may cause
         * unnecessary wake-ups, but only when there are multiple
         * racing acquires/releases, so most need signals now or soon
         * anyway.
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

独占式超时获取锁:

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }


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);
        }
    }

Condition
在这里插入图片描述

节点在队列之间的移动

在这里插入图片描述
公平锁和非公平锁的区别;
获取锁时多了这么一个判断

public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    
    }

非公平锁效率为什么高
公平锁等候队列里挨个Node 从唤醒到 启动 的时间是无法消除的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值