Java中的AQS AbstractQueuedSynchronizer

AbstractQueuedSynchronizer

什么是AQS

AbstractQueuedSynchronizer是 java同步中基础类 是用来实现同步的java

可是为何咱们在平时的使用中 却感受历来没有用到过这个类呢?缓存

这个是由于同步工具类中的内部类 继承AQS 这个内部把AQS封装起来 让咱们察觉不到这个类的存在安全

AQS中使用模板方法来实现同步机制 在Android中的View的onDraw onLayoutmarkdown

自定义同步工具类

若是咱们要实现独占 咱们须要实现 tryAcquire方法 并发

若是咱们要实现共享 咱们须要实现 tryAcquireShared方法ide

实现同步工具类关键是state这个变量 缺省都是0工具

显示锁都是继承Lock

那么咱们要实现 显示独占锁 就继承Lock性能

那么lock和unlock 要如何实现锁呢 这个时候就要借用AQS的ui

独占 不可重入的锁spa

public class SelfLock implements Lock {
    //内部类继承AQS
    private static class Sync extends AbstractQueuedSynchronizer {
        /*得到锁*/
        @Override
        protected boolean tryAcquire(int arg) {
            //经过设置指望state是0 而且设置state为1 为true的时候就表明拿到了锁
            if (compareAndSetState(0, 1)) {
                //告诉别人我拿到这个线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        /*释放锁*/
        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }
    private final Sync sync=new Sync();
    @Override
    public void lock() {
        //得到锁
        sync.acquire(1);
    }
    @Override
    public void unlock() {
        sync.release(1);
    }
复制代码

这样咱们就是实现了 同步锁

AQS的基本思想 CLH队列锁

一个代码块中 有一把锁, 任意时刻只有一个线程能拿到这把锁 其余线程都在外面排队 因此就是没有拿到锁的线程要在外面排队。

因此把排队的线程打包成QNode对象

QNode:

  • 前驱节点myPred
  • 当前线程
  • locked 须要去锁的时候为true

把排队的线程 QNode造成链表

例如: 假如线程A已经排队拿锁了,那么线程B要去拿锁就会在 将本身的myPred指向 A的QNode节点(而且locked=true)

那么线程是如何拿到锁的呢?

是这样由于 拥有前一个节点的 因此B就不断的自旋CAS 检查locked的值 若是locked=false了,那么就代码前一个线程A释放了锁会把本身的locked设置为false B线程就能够拿到锁了。

考虑到性能 AQS采用双向链表 并不会不断自旋 会尝试自旋必定次数后,将线程挂起

wait和notify notifyall 也有 一个 等待队列 跟这个拿锁的队列同样

公平锁和非公平锁

公平锁就是 全部的线程都要走排队的流程 CLH拿锁的流程就是 公平锁

公平锁代码以下:

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            //hasQueuedPredecessors 公平锁会判断是否有队列排队若是有就排队
                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;
        }
    }
复制代码

非公平锁就是 已经有队列了能够不排队直接插队拿锁就是非公平锁

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            //非公平锁,直接去拿锁
                if (compareAndSetState(0, acquires)) {
                    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;
        }

复制代码

可重入锁

可重入锁其实就是 state的状态变化

private static class RetainSync extends AbstractQueuedSynchronizer {
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            } else if (getExclusiveOwnerThread() == Thread.currentThread()) {
                setState(getState() + 1);
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if (getExclusiveOwnerThread() == Thread.currentThread()) {
                throw new IllegalMonitorStateException();
            }
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setState(getState() - 1);
            if (getState() == 0) {
                setExclusiveOwnerThread(null);
            }
            return true;
        }
    }
复制代码

JAVA内存模型 JMM Java Memory Model

由于Cpu 读取内存 须要100纳秒 cpu执行一条指令须要0.6纳秒

若是执行a+b 读取a 须要100纳秒 读取b也须要100纳秒 +须要0.6纳秒 总共执行时间为200.6纳秒

CPU就引入了一个高速缓存

CPU的高速缓存

L1 L2 L3

  • 工做内存包含 CPU寄存器 CPU缓存(99%) 主内存(1%) 主内存很是少

  • 主内存包含 CPU缓存(1%) 主内存(99%)(就是电脑内存条) cpu缓存很是少

工做内存是线程独享的

好比某个线程要对 int count 变量进行累加 count 存储在主内存中

就会在向这个线程的工做内存建立一个count的一个副本,线程不能操做主内存中的count, 只能操做工做线程中count副本

java内存模型带来的并发问题

能够看到 count=count+1; 因为工做内存的存在 这个方法并非安全的。 线程中的工做内存 对于同一个变量 是由可见性的问题的。 那么如何解决可见性问题
volatile 只能保证可见性 可是不能保证原子性。

volatile 能够对简单的赋值操做 可是对于复杂复合操做不能保证线程安全

volatile 能够应用在 一个线程写 多个线程读取的场景中使用

相关文章
相关标签/搜索