Juc07_乐观锁和悲观锁、公平锁和非公平锁、递归锁(可重入锁)、死锁及排查、自旋锁(一)

简介: ①. 乐观锁和悲观锁②. 公平锁和非公平锁

①. 乐观锁和悲观锁


  • ①. 悲观锁(synchronized关键字和Lock的实现类都是悲观锁)


  1. 什么是悲观锁?认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改


  1. 适合写操作多的场景,先加锁可以保证写操作时数据正确(写操作包括增删改)、显式的锁定之后再操作同步资源


  1. synchronized关键字和Lock的实现类都是悲观锁


  • ②. 乐观锁


  1. 概念:乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作


  1. 乐观锁在Java中通过使用无锁编程来实现,最常采用的时CAS算法,Java原子类中的递增操作就通过CAS自旋实现的


  1. 适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅度提升


  1. 乐观锁一般有两种实现方式(采用版本号机制、CAS算法实现)


③. 伪代码


  //悲观锁的调用方式
  public synchronized void m1(){
    //加锁后的业务逻辑
  }
  //保证多个线程使用的是同一个lock对象的前提下
  ReetrantLock lock=new ReentrantLock();
  public void m2(){
    lock.lock();
    try{
      //操作同步资源
    }finally{
      lock.unlock();
    }
  }
  //乐观锁的调用方式
  //保证多个线程使用的是同一个AtomicInteger
  private  AtomicInteger atomicIntege=new AtomicInteger();
  atomicIntege.incrementAndGet();


②. 公平锁和非公平锁


  • ①. 什么是公平锁和非公平锁


公平锁:是指多个线程按照申请锁的顺序来获取锁类似排队打饭先来后到


非公平锁:是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象


注意:synchronized 和 ReentrantLock 默认是非公平锁


②. 排队抢票案例(公平出现锁饥饿)


锁饥饿:我们使用5个线程买100张票,使用ReentrantLock默认是非公平锁,获取到的结果可能都是A线程在出售这100张票,会导致B、C、D、E线程发生锁饥饿(使用公平锁会有什么问题)


class Ticket {
    private int number = 50;
    private Lock lock = new ReentrantLock(true); //默认用的是非公平锁,分配的平均一点,=--》公平一点
    public void sale() {
        lock.lock();
        try {
            if(number > 0) {
                System.out.println(Thread.currentThread().getName()+"\t 卖出第: "+(number--)+"\t 还剩下: "+number);
            }
        }finally {
            lock.unlock();
        }
    }
    /*Object objectLock = new Object();
    public void sale(){
        synchronized (objectLock)
        {
            if(number > 0)
            {
                System.out.println(Thread.currentThread().getName()+"\t 卖出第: "+(number--)+"\t 还剩下: "+number);
            }
        }
    }*/
}
public class SaleTicketDemo {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() -> { for (int i = 1; i <=55; i++) ticket.sale(); },"a").start();
        new Thread(() -> { for (int i = 1; i <=55; i++) ticket.sale(); },"b").start();
        new Thread(() -> { for (int i = 1; i <=55; i++) ticket.sale(); },"c").start();
        new Thread(() -> { for (int i = 1; i <=55; i++) ticket.sale(); },"d").start();
        new Thread(() -> { for (int i = 1; i <=55; i++) ticket.sale(); },"e").start();
    }
}


③. 源码解读(ReentrantLock默认是非公平锁)


  1. 公平锁:排序排队公平锁,就是判断同步队列是否还有先驱节点的存在(我前面还有人吗?),如果没有先驱节点才能获锁


  1. 先占先得非公平锁,是不管这个事的,只要能抢获到同步状态就可以


  1. ReentrantLock默认是非公平锁,公平锁要多一个方法,所以非公平锁的性能更好(aqs源码)


微信图片_20220106185212.png


④. 为什么会有公平锁、非公平锁的设计?为什么默认非公平?面试题


恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间存在的还是很明显的,所以非公平锁能更充分的利用CPU的时间片,尽量减少CPU空闲状态时间


使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当一个线程请求锁获取同步状态,然后释放同步状态,因为不需要考虑是否还有前驱节点,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大了,所以就减少了线程的开销线程的开销


⑤. 什么时候用公平?什么时候用非公平?面试题


(如果为了更高的吞吐量,很显然非公平锁是比较合适的,因为节省很多线程切换时间,吞吐量自然就上去了。否则那就用公平锁,大家公平使用)




相关文章
|
移动开发 JavaScript 前端开发
开源项目推荐:SCADA组态软件Qt,kanzi,C#,MFC和WEB大全(收藏版)
开源项目推荐:SCADA组态软件Qt,kanzi,C#,MFC和WEB大全(收藏版)
6339 0
|
10月前
|
存储 API UED
鸿蒙特效教程02-微信语音录制动画效果实现教程
本教程适合HarmonyOS初学者,通过简单到复杂的步骤,一步步实现类似微信APP中的语音录制动画效果。
418 0
鸿蒙特效教程02-微信语音录制动画效果实现教程
Axios 通过a标签下载文件 跨域下载
Axios 通过a标签下载文件 跨域下载
Axios 通过a标签下载文件 跨域下载
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的实习生管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的实习生管理系统附带文章源码部署视频讲解等
105 4
|
算法 安全 UED
深入理解操作系统的虚拟内存管理
【4月更文挑战第29天】 在现代操作系统中,虚拟内存管理是一项关键技术,它允许每个运行的程序仿佛拥有独立的、连续的内存空间。本文将探讨虚拟内存的工作原理,包括分页机制、地址转换、页面置换算法以及虚拟内存带来的性能优势与挑战。通过对虚拟内存管理的深入分析,我们可以更好地理解操作系统如何有效地管理和分配有限的物理内存资源,以及如何通过技术手段解决内存碎片和程序隔离等问题。
|
消息中间件 监控 API
通过sts token 实现跨账户消费日志服务资源
阿里云账号可以通过创建并授权用户角色的方式赋予其他云账号一定的资源权限,其他云账号扮演该角色,并为其名下的RAM用户授予AssumeRole权限之后,其他云账号或其子账号可以通过访问STS接口获取临时AK和Token函数,调用日志服务API接口。
9848 1
通过sts token 实现跨账户消费日志服务资源
|
前端开发 C++
【Qt】实现显示指定路径目录结构
使用 Qt 的 QTreeView 显示指定路径下的目录结构
894 0
|
运维 专有云 云计算
云计算网络基础架构的实践和演进——打造云计算网络基石
从传统IT部署到云,人肉运维已经是过去式,云上运维该怎么开展?人工智能对于运维“威胁论”也随之袭来,如何去做更智能的活,当下很多运维人在不断思考和探寻答案。在2017云栖社区运维/DevOps在线技术峰会上,阿里云专家云登就为大家分享了云计算网络基础架构的实践和演进,精彩不容错过。
24620 0
|
4天前
|
存储 JavaScript 前端开发
JavaScript基础
本节讲解JavaScript基础核心知识:涵盖值类型与引用类型区别、typeof检测类型及局限性、===与==差异及应用场景、内置函数与对象、原型链五规则、属性查找机制、instanceof原理,以及this指向和箭头函数中this的绑定时机。重点突出类型判断、原型继承与this机制,助力深入理解JS面向对象机制。(238字)
|
3天前
|
云安全 人工智能 安全
阿里云2026云上安全健康体检正式开启
新年启程,来为云上环境做一次“深度体检”
1478 6