当前位置: 首页 > news >正文

Java 多线程 --- 锁的概念和类型划分

Java 多线程 --- 锁的概念和类型划分

  • 锁的概念
  • 乐观锁与悲观锁
  • 公平锁与非公平锁
  • 什么是可重入锁
  • 独占锁与共享锁
  • 轻量级锁和重量级锁
  • 自旋锁 (Spinlock)

锁的概念

  • 锁可以将多个线程对共享数据的并发访问转换为串行访问, 这样一个共享数据一次只能被一个线程访问, 该线程访问结束后其他线程才能对其进行访问.
  • 锁具有排他性 (Exclusive), 即一个锁一次只能被一个线程持有. 所以这种锁被称为排他锁或者互斥锁 (Mutex).

乐观锁与悲观锁

  • 乐观锁和悲观锁严格的说不是一种锁,而是一种策略

悲观锁

  • 加锁是一种悲观的策略,它总是认为每次访问共享资源的时候,总会发生冲突,所以宁愿牺牲性能(时间)来保证数据安全
  • 悲观锁的使用场景并不少见,数据库很多地方就用到了这种锁机制,比如行锁,表锁,读锁,写锁等,都是在做操作之前先上锁,悲观锁的实现往往依靠数据库本身的锁功能实现。Java程序中的Synchronized和ReentrantLock等实现的锁也均为悲观锁。

乐观锁

  • 乐观锁就是先不加锁. 无锁是一种乐观的策略,它假设线程访问共享资源不会发生冲突,所以不需要加锁,因此线程将不断执行,不需要停止。一旦碰到冲突,就重试当前操作直到没有冲突为止
  • 无锁的策略之一就是使用CAS机制

CAS机制

  • CAS的全称是Compare-and-Swap,也就是比较并交换,它包含了三个参数:V,A,B,
  • V表示要读写的内存位置,A表示旧的预期值,B表示新值
  • 具体的机制是,当执行CAS指令的时候,只有当V的值等于预期值A时,才会把V的值改为B,如果V和A不同,有可能是其他的线程修改了,这个时候,执行CAS的线程就会不断的循环重试,直到能成功更新为止
    在这里插入图片描述
  • CAS算是比较高效的并发控制手段,不会阻塞其他线程。但是,这样的更新方式是存在问题的,看流程就知道了,如果C的结果一直跟预期的结果不一样的话,线程A就会一直不断的循环重试,重试次数太多的话对CPU也是一笔不小的开销。
  • CAS的ABA问题
  • CAS还有个问题就是ABA问题,比如第一次拿到内存里的值时是A,然后被其他线程修改为B, 然后又修改为A, 而此时去比较内存里的值会发现没有变,但是实际上还是有改动
  • 举个通俗点的例子,你倒了一杯水放桌子上,干了点别的事,然后同事把你水喝了又给你重新倒了一杯水,你回来看水还在,拿起来就喝,如果你不管水中间被人喝过,只关心水还在,还好 ; 但是假若你是一个比较讲卫生的人,那你肯定就不高兴了
  • ABA问题的解决思路: 使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A了

公平锁与非公平锁

公平锁

  • 公平锁的概念是如果当前一个线程已经获取到锁了,其他线程如果再想获取到锁的话需要排队.

非公平锁

  • 非公平锁的概念是如果当前一个线程已经获取到锁了,那么新来的线程如果再想获取到锁先CAS抢一下,如果抢到了就执行代码,抢不到在去排队

Java中的公平锁和非公平锁

  • JDK中的ReentrantLock既支持非公平锁又支持公平锁,默认非公平锁
  • Synchronized则是非公平锁

优缺点

  • 公平锁的优点是等待锁的线程不会饿死。缺点是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU 唤醒阻塞线程的开销比非公平锁大
  • 非公平锁的优点是可以减少唤起线程的开销,整体的吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU 不必唤醒所有线程。缺点是处于等待队列中的线程可能会饿死,或者等很久才会获得锁

什么是可重入锁

  • 所谓重入锁,即一个线程如果获取到了锁,那么这个线程再下一次进入同步代码中的时候可以直接进入,不用重新获取锁,
  • 我们最熟悉的sychronized和ReentrantLock都是可重入锁。其实从ReentrantLock的名称上就可以看出来,Reentrant这个单词翻译成中文就是可重入的意思.
  • ReentrantLock可重入锁的实现,记录一下当前获取锁的线程记录为ownerThread,如果当前线程在获取锁的时候,发现自己就ownerThread,那么当前线程可以不用去抢锁直接执行

独占锁与共享锁

  • 独占锁的概念是如果有一个线程已经获取到了锁,其他线程不可以继续获取锁,锁只能有此线程独占。
  • 共享锁的概念是一个锁可以有多个线程共享,即一个线程获取到了锁,其他线程还可以继续获取锁
  • 基于AQS实现的ReentrantLock就是独占锁,而AQS也提供了实现共享锁的模版方法tryAcquireShared.

轻量级锁和重量级锁

  • 重量级锁的概念是如果锁已经被持有了,当前线程获取不到锁,当前线程挂起,等待锁的释放以及被唤醒。
  • 轻量级锁的概念是如果锁已经被持有了,当前线程获取不到锁,那么将使用CAS机制或者自旋的方式获取锁 (在Java中Synchronized的轻量级锁是用自旋锁实现的)
  • 这样设计的原因是大部分情况下我们占用锁的线程很快就执行完了,在很短的时间内就释放了锁,
  • 如果使用重量级锁,那么下一个线程想获取锁继续执行的话需要经历挂起以及唤醒,这个过程需要CPU上下文切换,这个时间开销甚至大于用户代码执行的时间,所以轻量级锁让线程等一会,锁一旦释放,当前线程可以立马获取到,省去了不必要的上下文切换的开销
  • JVM对Synchronized锁的优化就是从无锁到重量级锁的升级过程
  • 无锁->偏向锁->轻量级锁->重量级锁

自旋锁 (Spinlock)

  • 自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
  • 获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。
http://www.lryc.cn/news/26652.html

相关文章:

  • python程序员狂飙上头——京海市大嫂单推人做个日历不过分吧?
  • 浅谈子网掩码、IP地址、网络地址之间关系
  • 前端优化的解决方案
  • PYthon组合数据类型的简单使用
  • 【Java】P2 基础语法与运算符
  • 【并发基础】Java中线程的创建和运行以及相关源码分析
  • Spark Shuffle
  • Linux/MacOS 生成双击可执行文件
  • Ubuntu三种拨号方法
  • Vue-router的引入和安装
  • 无线WiFi安全渗透与攻防(四)之kismet的使用
  • 2023新版PMP考试有哪些变化?
  • P8074 [COCI2009-2010#7] SVEMIR 最小生成树
  • 10种常见网站安全攻击手段及防御方法
  • 为什么我选择收费的AdsPower指纹浏览器?
  • Java输入输出和数组
  • 这些免费API帮你快速开发,工作效率杠杠滴
  • 干货|最全PCB布线教程总结,14条PCB布线原则技巧,保姆级搞定PCB布线
  • 编程快捷键和markdown语法小计
  • 内网vCenter部署教程二,最全的了!
  • 2023-3-2 刷题情况
  • Docker SYS_ADMIN 权限容器逃逸
  • 【Kotlin】 yyyy-MM-dd HH:mm:ss 时间格式 时间戳 全面解读超详细
  • git repack多包使用及相关性能测试
  • QT获取dll库文件详细信息
  • 常见的电脑运行卡顿原因及解决方法
  • 案例08-让软件的使用者成为软件的设计者
  • QinQ与Vlan Mapping讲解
  • golang 获取token方法
  • 【数据库专题】数据库Mongodb之深入认知云计算三种服务方式、mongodb特点、mongodb重要进程 mongod、mongo、其他进程区别