Java多线程4—AQS


自旋锁指多线程下,当一个线程尝试获取锁的时候,如果锁被占用,则在当前线程循环检查锁是否被释放,此时当前线程并没有休眠或挂起。

1. 锁

在前面提到了synchronized关键字,其也是Java实现的一种锁机制,但本人认为其并不能实现广义锁的多种特性(公平性、乐观性等),因此将锁的介绍放在此处。

通常情况下,锁用来将某个对象或某段代码标记,使得JVM在程序执行时根据锁的状态来实现特定操作,而锁的状态由分为很多种,并且也能从不同的角度来讨论锁。

1.1 自旋锁与互斥锁

  • 自旋锁:尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁
    • 优点是减少线程上下文切换的消耗,因为线程不会阻塞,也就不会产生内核态和用户态的切换
    • 缺点是长时间循环获取会无谓的CPU消耗
  • 互斥锁:线程获取资源前必须获得该资源的锁,否则无法操纵资源
    • 优点是确保了资源能被某线程安全的访问
    • 缺点是可能造成死锁,且加锁/解锁开销较大

1.2 乐观锁与悲观锁

  • 乐观锁:认为读多写小,遇到并发写的可能性低。指对资源的访问不会上锁,而在更新前判断在操作过程中有没有线程已经更新过此数据
  • 悲观锁:认为读少写多,遇到并发写的可能性高。在访问资源时一定会对资源上锁,其他线程在锁释放之前无法访问

1.3 公平锁与非公平锁

我们知道多线程下必然存在某个线程在执行,多个线程在等待(就绪态),在线程执行完毕后等待线程中出现一个线程执行,而根据挑选的原则,将锁分为公平锁和非公平锁

  • 公平锁:就绪队列中的线程按照申请锁的顺序来获取锁(等待时间长短)
  • 非公平锁:与公平锁不同。JVM按照随机、就近原则给等待线程分配锁

1.4 共享锁与独占锁

  • 独占锁:指某个线程独占一个锁,独占期间其他线程无法访问,是一种悲观锁,互斥锁就是独占锁的一种实现
  • 共享锁:指允许多个线程共享某资源,是一种乐观锁

1.5 分段锁

分段锁是一种设计思想,通过对要访问的内存分段进行加锁来实现粒度更小的保护。

比如在Java1.7的ConcurrentHashMap实现中就利用了分段锁。

ConcurrentHashMap内部通过数组+链表时间,当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。


2. 自旋锁基础之CAS自旋

2.1 CAS介绍

Compare And Swap ========>compareAndSwapInt,它是一条CPU并发原语它的功能时判断内存的某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的

系统原语: 由若干条指令组成,用于完成某个功能的一个过程,并且原语的执行不能被打断。

2.2 CAS的优缺点

  • CASS是一种乐观锁,其具有非阻塞性,对死锁问题天然免疫,在高并发下,比其他锁有更好的实现
  • 一般情况下开销更小,但可能存在饥饿现象(线程数较多,等待时间长)
  • ABA问题:A变成B再变成A,解决方案有
    • 版本号
    • 使用JDK1.5后的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。(还是版本号)
  • 只能保证一个共享变量的原子操作:但从Java1.5开始 JDK 提供了 AtomicReference类 来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

3. AQS

AbstractQueuedSynchronizer(AQS)这个抽象类,是Java并发包 java.util.concurrent 的基础工具类,是实现 ReentrantLock、CountDownLatch、Semaphore、FutureTask 等类的基础,实现了除了java自带的synchronized关键字之外的锁机制

3.1 AQS的核心思想

如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中

AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进 入等待队列,等待被唤醒。

3.2 CLH锁

CLH锁是基于链表实现的自旋锁,它不断的轮询前驱的状态,如果前驱释放锁,它就结束自旋转。

CLH锁实现如下:

  • 线程持有自己的node变量,node中有一个locked属性,tru
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值