【腾讯阿里最全面试题】介绍下Synchronized、Volatile、CAS、AQS,以及各自的使用场景

本文详细介绍了Java并发编程中的关键概念,包括Synchronized、Volatile、CAS(Compare And Swap)以及AbstractQueuedSynchronizer(AQS)。内容涵盖了锁的原理、AQS的实现方式、锁的获取与释放过程,以及ReentrantLock的工作机制。此外,文章还讨论了锁在实际应用中的选择,如ReentrantLock与synchronized的区别,以及在低锁冲突场景下使用CAS的优势。通过对AQS的分析,读者能够更好地理解Java并发控制的底层机制。
摘要由CSDN通过智能技术生成

【腾讯阿里最全面试题】介绍下Synchronized、Volatile、CAS、AQS,以及各自的使用场景(文章较长,建议收藏观看)

相关视频讲解:

面试中出现概念最高的技术-原来就是这个锁

后台开发中必备技能—锁;原子操作 CAS

线程锁、进程锁、分布式锁以及数据库锁

锁概述

谈到并发,不得不谈ReentrantLock;而谈ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)!

类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch...。

lock实现过程中的几个关键词:计数值、双向链表、CAS+自旋

lock的存储结构:一个int类型状态值(用于锁的状态变更),一个双向链表(用于存储等待中的线程)

lock获取锁的过程:本质上是通过CAS来获取状态值修改,如果当场没获取到,会将该线程放在线程等待链表中。

lock释放锁的过程:修改状态值,调整等待链表。

可以看到在整个实现过程中,lock大量使用CAS+自旋。因此根据CAS特性,lock建议使用在低锁冲突的情况下。目前java1.6以后,官方对synchronized做了大量的锁优化(偏向锁、自旋、轻量级锁)。因此在非必要的情况下,建议使用synchronized做同步操作。

Abstract Queued Synchronizer (AQS)

 

AbstractQueuedSynchronizer 维护了一个state(代表了共享资源)和一个FIFO线程等待队列(多线程竞争资源被阻塞时会将线程放入此队列)。
由于state是由volatie修饰的所以该变量的改动都是立等可见的。

AQS 定义了两种资源共享的方式 Exclusive(独占,一时间只有一个线程能访问该资源)、Share (共享,一时间可以有多个线程访问资源).

  • 独占: 假设state初始状态为0,表示未锁定状态。线程A想使用该资源就把state修改为了1,那么线程B来访问资源时发现state是1并不是0他就会被AQS送入等待队列,
    直到线程A将该资源设置为0。
  • 共享:假设state初始状态为N,当有线程来访问后N就减少1个,直到N=0 这时就会阻塞新的线程来访问资源。当某一个线程执行完毕后会将state+1,相当于释放了该线程持有的锁。这样新的线程就可以继续访问该资源。

独占模式就像共享单车一时间只有一个人可以骑这个共享单车,共享模式就像公交车可以上去很多人,但是人一旦上满了就不能在上人了,必须要等车上的人下来后才能继续上人。

 


 

Example Diagram Analysis

Here's a retrospective. Let's take a simple example. If you don't understand something above, here's another chance to help you understand it.

First, the first thread calls reentrantLock.lock(), and when you turn to the front, you can see that tryAcquire(1) returns true directly and ends. It's just state=1, not even the head is initialized, let alone the blocking queue. If thread 1 calls unlock(), then thread 2 comes, then the world is peaceful and there is no intersection at all, then why do I need AQS?

If thread 1 did not call unlock(), thread 2 called lock(), think about what would happen?

Thread 2 initializes head [new Node()], while thread 2 inserts into the blocked queue and hangs (note that this is a for loop, and the part that sets head and tail is not return ed, only if the queue is successful will it jump out of the loop)

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

First, thread 2 initializes the head node, at which time headtail, waitStatus0

 

Thread 2 then queues up:

 

At the same time, we also need to look at the waitStatus of the node at this time. We know that the head node is initialized by thread 2. At this time, waitStatus is not set. java will be set to 0 by default. But when the shouldParkAfterFailed Acquire method is used, thread 2 will set the precursor node, that is, the waitStatus of the head, to -1 .

What is the waitStatus of the thread 2 node at this tim

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值