抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

AQS

AbstractQueuedSynchronizer, 和 CAS 一样, 也是Java并发编程的一个基石.

Java提供的同步器开发框架.

将用户和真正意义上的底层隔离 – 用户实现同步控制算法时不再需要使用 JVM 提供的最底层的 API.

有临界区要保护. 互斥量(Mutex); 信号量(Semaphore)。

Java 线程是用户到内核映射, 内核级线程, 调度的是OS。

互斥: 允许临界区一个线程进入.

信号量: 允许临界区N个线程进入. 也就是并发量等于1的就是互斥量.

Condition: 条件变量, 是操作系统提供的, 基于整数的, 生产者/消费者的模型.

总结

  • 基于 Java 实现.
  • 提供同步元语和高性能生产/消费者结构.
  • 提供Non-Blocking能力(CAS/TryLock).
  • 提供定时能力.
  • 提供中断能力.
  • 提供线程间协作(Condition).
  • 提供扩展数据结构的能力.
  • 内部提供整数状态.
    • 封装CAS操作. acquire/release 都基于cas状态; cas失败触发在等待队列中等待.
    • 封装高性能CLH队列. CAS失败自动进入队列; 条件等待自动进入队列.

CLH队列 是线程安全的, 同时也是无锁编程(Lock-Free)的.

性能层面

  • 先竞争进入队列,成本低。
  • 再进入临界区是排队进去的。

里面还有公平非公平的思考.

AQS 工作原理

加锁工作原理。

void acquire() {
    // 尝试获取 所以继承的话要重写 tryAcquire() tryRelease() 方法
    while(!tryAcquire()){
        // 如果队列中没有当前线程
        queue.add(currentThread); // 加入队列
        park(); // 休眠线程
    }
}

控制反转(IOC)的, 获取锁的逻辑由开发者提供。

竞争分为2个阶段. while: 部分进行自旋竞争; 竞争失败的在队列中休眠(park), 直到竞争成功者退出(unpark)

void acquire(){
    while(!tryAcquire()){
        // 如果队列中没有当前线程: 加入队列
        queue.add(currentThread);
        pack(); // 休眠线程
    }
}
// 用户实现继承类重写这个方法
@Override
boolean tryAcquire(){
    // CAS 获得锁的逻辑
    return 获得锁是否成功
}

公平性

什么是公平?

  • FIFO 先入先出.
  • Random 随机.

公平锁: 除了第一个线程, 其余线程都会阻塞, cpu唤醒线程的开销会更大。

非公平锁: 多个线程去获取锁的时候, 会直接去尝试获取.如果能获取到, 直接获得锁, 不用再阻塞

后来者能抢占(插队), 就是不公平的. 所以不公平的插队, 可以让插队线程跳过陷入阻塞的过程, 意味着不公平锁更快, 吞吐量更大。

ReentrantLock 实现了2个.

公不公平看获取锁的逻辑. 不公平, 允许新进来的线程进入竞争。

// ReentrantLock 
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    /**
        * Acquires only if reentrant or queue is empty. 只能在队列是空或者可重入时获取
        */
    final boolean initialTryLock() {
        Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 如果有排队的线程, 直接进行休眠
            // hasQueuedThreads 如果有线程在等待就返回 true
            if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        } else if (getExclusiveOwnerThread() == current) {
            if (++c < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(c);
            return true;
        }
        return false;
    }

    /**
        * Acquires only if thread is first waiter or empty. 仅当线程是第一个等待者或者为空时才获取.
        */
    protected final boolean tryAcquire(int acquires) {
        if (getState() == 0 && !hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
}

总结:如果是公平锁, 一旦已经有线程在排队了, 就不再尝试获取锁了; 对于非公平锁而言, 无论是否有线程在排队, 都会尝试获取一下锁, 获取不到再去排队。

CLH 队列实现

基于链表+CAS实现. 尾部插入, 头部删除.

  • 避免了双向链表
  • 单向链表性能好

评论