首页 > 其他分享 >juc--三大接口

juc--三大接口

时间:2023-06-12 16:33:00浏览次数:50  
标签:juc 队列 写锁 -- 读写 获取 线程 三大



文章目录

  • juc
  • 一、为什么会有juc
  • 二、juc--三大接口
  • 1.lock
  • 2.condition
  • 3.ReadWriteLock
  • 二、juc--的默认实现类
  • 1.ReentrantLock--lock的默认实现类
  • 公平锁,非公平锁
  • 2. ReentrantReadWriteLock读写锁--ReadWriteLock的默认实现类
  • 1 读写锁和排它锁
  • 2 读写锁原则
  • 总结



juc

juc: java.util.concurrent juc可太牛掰了,几乎完成里面所有的逻辑代码,都是老爷子一个人完成的;


一、为什么会有juc

因为synchronized在很多场景中是无法完美契合的,为了弥补这个缺陷,juc应运而生;说一个最简单的点:synchronized无法知道我是否获取锁成功,无法主动释放锁,当临界区的代码有问题的时候,就会一直阻塞!
synchronized就那么一无是处嘛? no , 例如: synchronized是肯定不会发生死锁的,但是juc中的可重入锁,当时用不当(加锁与解锁没有成对出现)的时候,就会发生死锁!

二、juc–三大接口

juc--三大接口_读锁

1.lock

juc--三大接口_分布式_02


源码中的示例代码如上:

  • 加锁 lock.lock();
  • 释放锁 lock.unlock(); 一般在finally 中; 目的是可以最终释放锁,不会导致发生意外后,一直阻塞
  • lock 一般不会单独使用

2.condition

juc--三大接口_读锁_03


Condition源码示例代码如上:

  • 与lock 配合使用
  • 当使用lock 加锁解锁后, 可以用Condition来作为线程之间的通信
  • Condition中的线程通信方法 和Object的wait/notify基本相似。其中,Condition的await方法对应的是Object的wait方法,而Condition的signal/signalAll方法则对应Object的notify/notifyAll()。但Condition类似于Object的等待/通知机制的加强版。
  • juc--三大接口_java_04

  • Condition 中有两种队列,分别为 同步队列 和 等待队列
  1. 同步队列为一个双向队列
private transient volatile Node head;
  private transient volatile Node tail;

juc--三大接口_数据库_05

每次加入的时候,把线程包装成一个node(包含 获取同步状态失败的线程引用、等待状态以及前驱和后继节点),每次都是根据tail 找到最后一个节点,然后加入,并将tail 指向最后加入的元素; 利用CAS保证安全;

  1. 调用了await之后,当前线程会进入等待队列 ,每次new 的Condition都是一个等到队列,等待队列是一个链式的单项队列,它会把同步队列中的头节点删除并移动到这个等待队列中
nextWaiter;

用Object的方式(wait notify)实际上是指在对象Object对象监视器上只能拥有一个同步队列和一个等待队列,而并发包中的Lock拥有一个同步队列和多个等待队列;
使用 Condition 的时候,多个线程必须获取的是同一把lock ,这样才能调用await 和 singal 等方法;

3.ReadWriteLock

这个可就牛掰了~~,读写锁;

juc--三大接口_公平锁_06


可以获取到读锁和写锁; 适用于读多写少的情况;

  • 读锁,允许多个线程同时访问
  • 写锁,可以理解为是synchronized, 临界区的代码同一时间只能有一个线程访问;

二、juc–的默认实现类

1.ReentrantLock–lock的默认实现类

public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

这个锁默认是非公平的锁,且初始化之后无法改变,只能通过 isFair() 获取当前锁的属性;

公平锁,非公平锁

  • NonfairSync和FairSync,分别是非公平锁和公平锁;
  • 这里的“公平”,其实通俗意义来说就是“先来后到”,也就是FIFO。如果对一个锁来说,先对锁获取请求的线程一定会先被满足,后对锁获取请求的线程后被满足,那这个锁就是公平的。反之,那就是不公平的。

一般情况下,非公平锁能提升一定的效率。但是非公平锁可能会发生线程饥饿(有一些线程长时间得不到锁)的情况。所以要根据实际的需求来选择非公平锁和公平锁。
ReentrantLock支持非公平锁和公平锁两种。

2. ReentrantReadWriteLock读写锁–ReadWriteLock的默认实现类

它与ReentrantLock的功能类似,同样是可重入的,支持非公平锁和公平锁。不同的是,它还支持”读写锁“。
有一个弊端: 使用读写锁,在写线程访问时,所有的读线程和其它写线程均被阻塞。

1 读写锁和排它锁

我们前面讲到的synchronized用的锁和ReentrantLock,其实都是“排它锁”。也就是说,这些锁在同一时刻只允许一个线程进行访问。通常说法的锁其实都是排他锁;

而读写锁可以在同一时刻允许多个读线程访问。Java提供了ReentrantReadWriteLock类作为读写锁的默认实现,内部维护了两个锁:一个读锁,一个写锁。通过分离读锁和写锁,使得在“读多写少”的环境下,大大地提高了性能。

2 读写锁原则

  • 读写锁允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞。
  • 同步状态的高16位用来表示读锁被获取的次数,同步状态的低16位用来表示写锁的获取次数; 高低16位分别维护读写锁的次数;
  • 当读锁已经被读线程获取或者写锁已经被其他写线程获取,则写锁获取失败;否则,获取成功并支持重入,增加写状态;

为什么读锁已经被读线程获取,会导致写锁获取失败?
当读锁已经被读线程获取 写锁获取失败的原因是: 写锁必须对读锁保证其可见性,如果获取写锁成功则无法保证其对于读线程的可见性

  • 当写锁被其他线程获取后,读锁获取失败,如果可重入锁,那么写线程如果获取同一把锁,仍会获取成功;
  • 锁降级是写锁降级成为读锁,是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。

如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级


总结

juc几乎提供了所有多线程下的,线程通信机制,完美解决了线程中各种场景的问题;


标签:juc,队列,写锁,--,读写,获取,线程,三大
From: https://blog.51cto.com/u_16158506/6463458

相关文章

  • CAS简介
    文章目录前言一、锁?CAS?二、juc--java.util.concurrent1.CAS思想的落地2ABA问题出现3可以解决的问题4缺点总结前言CAS的全称是:比较并交换(CompareAndSwap)。在CAS中,有这样三个值:V:要更新的变量(var)E:预期值(expected)N:新值(new)比较并交换的过程如下:判断V是否等于E,如果等于......
  • 2020-09-10 mysql主从复制
    mysql主从复制解决问题:高并发,灾难恢复,读写分离,故障转移mysql01mysql02数据实时同步:是通过执行的dmlsql语句(包括增删改),写入到二进制日志binlog文件中,来实现数据同步的.从数据库开启一个io线程读取主数据库中的binlog文件,读取到后,开启一个sql线程,执行binlog文件.达......
  • clickhouse 为什么快?
    文章目录@[TOC](文章目录)前言一、什么是列式数据库?为什么要用列式数据库,优点是什么?二、clickhouse入门1.个人猜想2.使用clickhouse引入依赖yml配置扫描mapper2.生成相应代码,执行测试用例查询结果总结前言例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启......
  • 2020-07-03 java配置环境变量
    java开发,首先要安装JDK,并配置环境1 安装JDK(本人下载的安装包,无脑下一步,选择了下文件夹),安装完成截图如下2 开始配置环境变量右键我的电脑==高级系统设置==环境变量==系统变量中选择新建 (1)JAVA_HOME路径根据自己安装的写,路径到(bin 上一层)例如:笔者的jdk 的bin......
  • 2020-10-26 多线程学习1
    join关键字测试:publicclassTestJoin{publicstaticvoidmain(String[]args)throwsInterruptedException{//TODOAuto-generatedmethodstubfor(inti=0;i<3;i++){ThreadTestt1=newThreadTest("A");......
  • 终端上显示时间,可以这么玩
    没有入门的同学,请看上一篇,这个说能够修改的。长时间在终端下工作的同学,可能都把终端配置成真正的全屏,满都显示,不能像有些系统一样自以为是模,那样很让人讨厌的。那么,总不能还要时不时去看时间吧。这个看我的吧1、这个命令:whilesleep1;dotputsc;tputcup0$(($(tputcols)-1......
  • 315. 计算右侧小于当前元素的个数
    labuladong题解难度困难987给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。 示例1:输入:nums=[5,2,6,1]输出:[2,1,1,0]解释:5的右侧有2个更小的元素(2和......
  • 手写 Django orm反向迁移 MySQL
    importpymysql,os####settingsdb={'NAME':'','USER':'','PASSWORD':'','HOST':'','PORT':'',}table_name_list=[]#表名列表......
  • frp内网穿透web服务配置
    frp是使用较多的免费开源的内网穿透软件,源代码托管在GitHub。1.下载安装安装步骤可参考官方文档https://gofrp.org/docs/setup/点击项目的release地址进入下载页面:https://github.com/fatedier/frp/releases,首先要根据自己需要安装的机器系统架构下载相应的二进制安装包。frp的......
  • SpringBoot多模块项目搭建以及搭建基础模板
    多模块项目搭建目录多模块项目搭建1.父项目pom文件编辑2.创建子模块1.父项目pom文件编辑<!--1.父工程添加pom格式--><packaging>pom</packaging><!--定义子模块--><modules><module>walker-service</module><module>walker-utils&......