带你快速搞定java多线程(4)

 更新时间:2021年07月14日 17:01:33   作者:香菜聊游戏  
这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下

1、AQS 是什么?

AQS 是类 AbstractQueuedSynchronizer的简称,也是常用锁的基类,比如常见的ReentrantLock,Semaphore,CountDownLatch 等等。

AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架。是Java提供的一种模板,一般在现有同步器无法完成的时候可以自行扩展。当然也可以自己实现,不过既然有现成的为什么还要自己瞎鸡儿写。

图片

2、AQS 模型

图片

注意:图来自网上,具体的原地点在哪我也不知道。如果有问题可以联系我。

解释:整个模型相当于在食堂吃饭,只开了一个窗口,只有一个打饭的师傅(state),所有想要吃饭的线程必须排队等待,直到轮到自己。

AQS就是基于队列,用共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。代码解决现实问题,现实问题催生解决方案。

3、AQS state

state 代表 共享资源和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。

state的访问方式有三种:

  • getState()
  • setState()
  • compareAndSetState()

4、AQS 两种资源共享方式:

1.Exclusive:独占,只有一个线程能执行,如ReentrantLock

2.Share:共享,多个线程可以同时执行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier。

5、模板方式实现自定义

不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义时主要实现以下几种方法:

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
  • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock。

6、锁的分类:公平锁和非公平锁,乐观锁和悲观锁

  • 公平锁:当线程A获取访问该对象,获取到锁后,此时内部存在一个计数器num+1,其他线程想访问该对象,就会进行排队等待(等待队列最前一个线程处于待唤醒状态),直到线程A释放锁(num = 0),此时会唤醒处于待唤醒状态的线程进行获取锁的操作,一直循环。如果线程A再次尝试获取该对象锁时,会检查该对象锁释放已经被占用,如果还是当前线程占用锁,则直接获得锁,不用进入排队。
  • 非公平锁:当线程A在释放锁后,等待对象的线程会进行资源竞争,竞争成功的线程将获取该锁,其他线程继续睡眠。

公平锁是严格的以FIFO的方式进行锁的竞争,但是非公平锁是无序的锁竞争,刚释放锁的线程很大程度上能比较快的获取到锁,队列中的线程只能等待,所以非公平锁可能会有“饥饿”的问题。但是重复的锁获取能减小线程之间的切换,而公平锁则是严格的线程切换,这样对操作系统的影响是比较大的,所以非公平锁的吞吐量是大于公平锁的,这也是为什么JDK将非公平锁作为默认的实现。

  • 悲观锁:总是假设最坏的情况,每次想要使用数据的时候就恰好别人也要修改数据,一切是以安全第一,所以在每次操作资源的时候都会先加锁,不管有没有人抢,然后独占资源。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现
  • 乐观锁:乐观锁和悲观锁刚好相反,自己使用资源的时候没有人抢,所以不需要上锁。乐观锁的实现方案一般来说有两种:版本号机制 和 CAS实现 。乐观锁多适用于多度的应用类型,这样可以提高吞吐量。

7、CAS

CAS(Compare And Swap),即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。sun.misc.Unsafe 类提供了硬件级别的原子操作来实现这个CAS。java.util.concurrent 包下的大量类都使用了这个 Unsafe.java 类的CAS操作。Unsafe 包是C++的接口实现,直接可以访问计算机的内存等敏感操作,所以标记为Unsafe。

CAS 是直接调用计算机的硬件指令,是硬件级别的线程同步,也是一种乐观锁。

8、总结

从整体上说了下AQS,没有深入到代码层级,先理解思想,后看具体实现。你理解了吗?下期具体分析一个CountDownLatch 源码,从细节理解AQS。

本篇文章就到这里了,希望能对你有所帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • 使用eclipse + maven一步步搭建SSM框架教程详解

    使用eclipse + maven一步步搭建SSM框架教程详解

    SSM(Spring+SpringMVC+MyBatis)框架集由Spring、SpringMVC、MyBatis三个开源框架整合而成,常作为数据源较简单的web项目的框架.这篇文章主要介绍了eclipse + maven搭建SSM框架 ,需要的朋友可以参考下
    2017-11-11
  • Spring @Conditional注解讲解及示例详解

    Spring @Conditional注解讲解及示例详解

    这篇文章主要介绍了Spring @Conditional注解讲解及示例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • 浅谈java 中equals和==的区别

    浅谈java 中equals和==的区别

    这篇文章主要介绍了java 中equals和==的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • 详解Spring中的Transactional属性

    详解Spring中的Transactional属性

    今天我在写代码的时候,看到了一个注解@Transactional(rollbackFor = Exception.class),今天就和大家分享一下,这个注解的用法,,需要的朋友可以参考下
    2021-05-05
  • Java中创建线程池的几种方式以及区别

    Java中创建线程池的几种方式以及区别

    创建线程池有多种方式,主要通过 Java 的 java.util.concurrent 包提供的 Executors 工具类来实现,本文给大家介绍了几种常见的线程池类型及其区别,并通过代码示例讲解的非常详细,需要的朋友可以参考下
    2024-11-11
  • Java之HashMap.values()方法的误用解读

    Java之HashMap.values()方法的误用解读

    这篇文章主要介绍了Java之HashMap.values()方法的误用解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 基于Java语言的递归运算例题详解

    基于Java语言的递归运算例题详解

    一个方法在执行过程中调用自身, 就称为 "递归"。本文将通过几个例题带大家深入了解一下Java语言中的递归运算,感兴趣的可以了解一下
    2022-08-08
  • Spring 异常处理的各种姿势总结

    Spring 异常处理的各种姿势总结

    这篇文章主要介绍了Spring 异常处理,总结分析了Spring 异常处理的各种常见操作技巧与相关使用注意事项,需要的朋友可以参考下
    2020-05-05
  • Java面向对象基础,类,变量,方法

    Java面向对象基础,类,变量,方法

    这篇文章主要介绍了Java面向对象基础,类,变量,方法,需要的朋友可以参考下
    2020-10-10
  • idea本地merge如何合并代码

    idea本地merge如何合并代码

    这篇文章主要介绍了idea本地merge如何合并代码问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06

最新评论