8锁问题 场景一 标准情况访问:两个同步方法,一个对象调用 import java.util.concurrent.TimeUnit; /** * 标准情况下 是先sendEmail() 还是先callPhone 两个方法持有的是同一把锁,因此谁先拿到锁谁先执行。 public class LockDemo8 { public static void main(String[] args) throws InterruptedException { Phone8 phoneA = new Phone8(); Phone8 phoneB = new Phone8(); new Thread(()->{ new Thread(()->{ phoneB.callPhone(); },"B").start(); } } class Phone8{
8锁现象,其实就是关于锁的八个问题,本节我们通过八个问题,逐渐带大家加深对锁的认识,为了方便演示,这里全部用synchronized演示。 synchronized method -> 玩耍 synchronized method -> 吃饭 总结:synchronized锁的是方法的调用者,这里就是people1,不同的对象对应不同的锁 synchronized method-> 学习 static synchronized method-> 吃饭 问题8:不同对象调用1个静态同步方法,1个同步方法,先执行哪个?
到底锁的谁?要么锁new出来的对象,要么是Class,下面详解) (8锁就是关于锁的8个问题) 一、问题1和2 1、问题 ①标准情况下,两个线程是先打印发短信还是打电话? 8锁:关于锁的8个问题 * 1、标准情况下,两个线程是先打印发短信还是打电话? 2、代码、注释及运行结果 代码和注释: package com.zibo.lock8; import java.util.concurrent.TimeUnit; /** * 8锁:关于锁的8个问题 2、代码、注释及运行结果 代码和注释: package com.zibo.lock8; import java.util.concurrent.TimeUnit; /** * 8锁:关于锁的8个问题 2、代码、注释及运行结果 代码和注释: package com.zibo.lock8; import java.util.concurrent.TimeUnit; /** * 8锁:关于锁的8个问题
这篇文章跟大家一起聊聊MySQL的8种锁,希望对你会有所帮助。 一、锁的本质:并发控制的基石 1.1 为什么需要锁? 二、锁的分类全景图 2.1 按粒度划分 按粒度划分为: 表锁 页锁 行锁 2.2 按模式划分 锁类型 共享性 典型场景 共享锁(S) 可共享 SELECT ... LOCK IN SHARE MODE 排他锁(X) 独占 UPDATE/DELETE/INSERT 意向共享锁(IS) 表级标记 准备加行级S锁前 意向排他锁(IX) 表级标记 准备加行级X锁前 三、 工具 总结 锁是双刃剑:保护数据一致性的同时降低并发度 粒度决定性能:行锁 > 页锁 > 表锁 隔离级别是基础:根据业务选择合适级别(推荐RR) 索引是钥匙:80%的锁问题可通过优化索引解决 监控是眼睛 最高明的锁策略是“无锁”,而这正是我们不断优化的方向。
FTWRL 对于备份的意义在于,在我们操作这个命令的时候,会获取每个表的metadata lock , 此时获取表的lock 是逐步的过程,必须等待每个表的事务完成后,才能获得表元数据锁,并将锁的模式锁定到共享锁 实际上 FTWRL 做了以下几个工作 1 对所有的表上了全局的读锁, 2 清理了表缓存 3 上全局commit锁. 总结FTWRL ,几个步骤, 请求锁, 等待锁, 刷新表,持有锁.而我们今天要说的mysql 8.0 的LOCK INSTANCE FOR BACKUP 新特色, 其实在 PERCONA 5.6 版本的 "旧锁" 官方文档中对LOCK INSTANCE FOR BACKUP 获得一个instance level 的backup lock 锁, 可以在锁持有时进行DML 操作. 所以MYSQL 8 新备份的方式的改变是通过LOCK INSTANCE for BACKUP 和 log_status 联合完成的, 基于MYSQL 8 的第三方备份软件等都需要对此进行研究并改变目前的备份的方式
Lab 8: Locks 重新设计代码以降低锁竞争,提高多核机器上系统的并行性。 锁竞争优化一般有几个思路: 只在必须共享的时候共享(对应为将资源从 CPU 共享拆分为每个 CPU 独立) 必须共享时,尽量减少在关键区中停留的时间(对应“大锁化小锁”,降低锁的粒度) 该 lab 的实验目标 14: 上述代码可能产生死锁(cpu_a 尝试偷 cpu_b,cpu_b 尝试偷 cpu_a),可能的解决方案看本文评论区或 https://github.com/Miigon/blog/issues/8。 前面提到的: 锁竞争优化一般有几个思路: 只在必须共享的时候共享(对应为将资源从 CPU 共享拆分为每个 CPU 独立) 必须共享时,尽量减少在关键区中停留的时间(对应“大锁化小锁”,降低锁的粒度) 这里,由于 CPU1 持有锁 2 的情况下去申请锁 5,而 CPU2 持有锁 5 的情况下申请锁 2,造成了环路等待。
乐观锁&悲观锁 1.1.1乐观锁&悲观锁 乐观锁和悲观锁只是锁的一种策略,并不是具体实现 乐观锁: 假设冲突概率低,先操作,更新时检查数据有没有被修改过,比如用版本号机制。 synchronized初始使用乐观锁策略,当发现锁竞争比较频繁的时候,,就会自动切换成悲观锁策略 1.1.2轻量级锁&重量级锁 锁的核心特性 “原子性”,这样的机制追根溯源是CPU这样的硬件设备提供的 1.1.3自旋锁&挂起等待锁 自旋锁是轻量级锁的具体实现,挂起等待锁是重量级锁的具体实现 自旋锁:自旋锁是一种忙等待的锁,当某线程尝试获取自旋锁时,如果该锁已经被其他线程持有,该线程不会陷入阻塞,而是会在一个循环中不断地检查锁是否被释放 1.1.6可重入锁/不可重入锁 可重入锁:线程已经持有某个对象的锁,那么它可以再次获取该对象的锁,不会被阻塞。 当计数器为零时,锁才真正被释放 不可重入锁:线程已经持有某个对象的锁,那么它可以再次获取该对象的锁,会被阻塞 1.2其他锁策略 锁消除 编译器+JVM 判断锁是否可消除.。
4.8 JDK8新增的StampedLock锁探究 StampedLock是并发包里面jdk8版本新增的一个锁,该锁提供了三种模式的读写控制,三种模式分别如下: 写锁writeLock,是个排它锁或者叫独占锁 ,同时只有一个线程可以获取该锁,当一个线程获取该锁后,其它请求的线程必须等待,当目前没有线程持有读锁或者写锁的时候才可以获取到该锁,请求该锁成功后会返回一个stamp票据变量用来表示该锁的版本,当释放该锁时候需要 悲观读锁readLock,是个共享锁,在没有线程获取独占写锁的情况下,同时多个线程可以获取该锁,如果已经有线程持有写锁,其他线程请求获取该读锁会被阻塞。 下面通过JDK8注释里面的一个例子讲解来加深对上面讲解的理解。 代码(1)获取悲观读锁,保证其它线程不能获取写锁修改x,y值,然后代码(2)判断如果当前点在原点则更新坐标,代码(3)尝试升级读锁为写锁,这里升级不一定成功,因为多个线程都可以同时获取悲观读锁,当多个线程都执行到
读锁不可以升级,写锁可以降级? 读锁是可并行的,写锁是串行的,那么如果多个读锁并行执行,遇到升级语句,就会出现死锁,比如t1要升级,那么就要等t2释放锁,而t2正好也在当t1释放锁。 =0,表示重入;这里有两种:读锁,写锁 if (c ! ,重入锁+1 setState(c + acquires); return true; } // 走到这里,表示c=0,即没有写锁,也没有读锁 if (writerShouldBlock 因为在读锁加锁的时候,也可能出现写锁进来,如果写锁进来了,写锁是排他锁,独占一把锁,那么读锁也要去排队 if (exclusiveCount(c) ! 因为在读锁加锁的时候,也可能出现写锁进来,如果写锁进来了,写锁是排他锁,独占一把锁,那么读锁也要去排队 if (exclusiveCount(c) !
公平锁和非公平锁 ReentrantLock 默认采用非公平锁,除非你在构造方法中传入参数 true 。 : 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态 公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。 相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。 需要深入了解这几个方法的读者,可以参考我的另一篇文章《深入分析 java 8 编程语言规范:Threads and Locks》。
今天突然被一个同事问到java8为何放弃分段锁,于是花了点时间针对这个问题进行了小小的总结。 关于分段锁 段Segment继承了重入锁ReentrantLock,有了锁的功能,每个锁控制的是一段,当每个Segment越来越大时,锁的粒度就变得有些大了。 当前容量超过阈值 当链表中元素个数超过默认设定(8个),当数组的大小还未超过64的时候,此时进行数组的扩容,如果超过则将链表转化成红黑树 什么时候链表转化为红黑树? 当数组大小已经超过64并且链表中的元素个数超过默认设定(8个)时,将链表转化为红黑树 ConcurrentHashMap的put操作代码如下: ? 把数组中的每个元素看成一个桶。 内部优化:synchronized则是JVM直接支持的,JVM能够在运行时作出相应的优化措施:锁粗化、锁消除、锁自旋等等。
测试JPA乐观锁 新建一个方法,如下 image.png 这里查询了两次id为1的记录,然后分别更新了这条记录。 总结 spring data jpa通过OptimisticLocking实现了乐观锁,该乐观锁不是通过数据库自身去实现的,它是通过version字段(需要Version注解标识)去实现的。
MYSQL 中有一个重要的特性就是锁,如何认识到锁的概念对于使用MYSQL有着重要的意义,针对与锁的认识,以及发现我们需要通过MYSQL本身的performance_schema 中的表来了解,不熟悉这一个系列的同学可以去从之前的 MYSQL的锁可以从 metadata 和 表锁开始。 下面的语句可以从performance_schema 中获得当前的查询中因为锁而正在锁定中的语句中所需的表,分为正在等待的访问的线程和正在阻挡这个线程运作的线程,当然可以稍微的在变化一下,就可以获得两个操作的语句 那么除此以外,我们在MYSQL的操作中的死锁的问题,怎么分析在MYSQL8 中祭出了表 1 data_lock_waits 2 data_locks 两个表 查询当前表中是否有死锁或锁的block,需要从 锁的信息标注了,产生的是X行锁。 以上的查询对于我们的分析死锁,以及成因有着很大的帮助。
“阅读本文大概需要8分钟。 前言 你好,我是测试蔡坨坨。 在前几篇Redis相关文章中都说到了锁,同时我们在参加设计评审或者codeReview时也会接触到关于加锁的问题。 共享锁也叫读锁,读锁的特点是在同一时刻允许多个线程抢占到锁。 排它锁也叫写锁,写锁的特点是在同一时刻只允许一个线程抢占到锁。 性能和线程安全 我们经常听到的乐观/悲观锁、自旋锁、可重入锁、偏向锁、轻量/重量级锁又是什么呢? 既然说到了乐观锁,不得不提一嘴悲观锁,乐观锁和悲观锁有什么区别呢? 公平锁/非公平锁 公平锁就是每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的;非公平锁就是每个线程获取锁的顺序是随机的,并不会遵循先来后到的规则,所有线程会竞争获取锁。
独占锁:指该锁一次只能被一个线程所持有。对ReentrantLock和Synchronized而言都是独占锁 共享锁:指该锁可被多个线程所持有。 对ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。 读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。 使用方法 声明一个读写锁 如果需要独占锁则加从可重入读写锁里得到写锁 写锁demo 如果需要共享锁则加从可重入读写锁里得到读锁 读锁demo ReentrantReadWriteLock实现原理简单分析 Sync是如何同时表示读锁与写锁? ,低16位表示写锁个数 一个线程获取到了写锁,并且重入了两次,低16位是3,线程又获取了读锁,并且重入了一次,高16位就是2 读锁的写锁的获取主要调用AQS的相关Acquire方法,其释放主要用了相关Release
总体上分成两种:乐观锁和悲观锁类型上也是两种:读锁和写锁 锁的粒度上可以分成五种:表锁,行锁,页面锁,间隙锁,临键锁 下面我们就来详细讲一下这些锁 1. 写锁 写锁又称为排他锁或者X锁(Exclusive Lock),如果当前写锁未释放,他会阻塞其他的写锁和读锁。 5. 表锁 表锁也称为表级锁,就是在整个数据表上对数据进行加锁和释放锁。 共享锁:允许一个事务读取一行数据,但不允许一个事务对加了共享锁的当前行增加排他锁。排他锁:允许当前事务对数据行进行增删改查操作,不允许其他事务对增加了排他锁的数据行增加共享锁和排他锁。 不过,随着锁定资源颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也随之提升。使用页级锁定的主要是 BerkeleyDB 存储引擎。8. 如果执行以下sql UPDATE user_user SET sex = 1WHEREid > 8ANDid < 18; 那么其他事务就无法在 (7,21] 这个区间内插入或者修改任何数据。
最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 在Java并发场景中,会涉及到各种各样的锁如公平锁,乐观锁,悲观锁等等,这篇文章介绍各种锁的分类: 公平锁/非公平锁 可重入锁 独享锁/共享锁 乐观锁/悲观锁 分段锁 自旋锁 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度 详情可以参考:阿里P8架构师谈:MySQL行锁、表锁、悲观锁、乐观锁的特点与应用 再比如上面提到的Java的同步synchronized关键字的实现就是典型的悲观锁。 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 3.总之: 悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。 我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现
文章目录一、概述1.1 MySQL锁的由来1.2 锁定义1.3 锁分类二、共享锁与排他锁2.1 共享锁(S锁)2.2 排他锁(X锁)2.3 MySQL锁的释放三、全局锁3.1 介绍 3.2 语法 3.3 )5.3 间隙锁(Gap Lock)5.4 临建锁(Next-Key Lock)5.5 行锁的粒度粗化六、页面锁、乐观锁与悲观锁6.1 页面锁6.2 乐观锁6.3 悲观锁七、加锁规则八、总结一、概述1.1 这里的元数据可以简单理解为一张表的表结构意向锁(分为意向共享锁、意向排他锁):这个是InnoDB中为了支持多粒度的锁,为了兼容行锁、表锁而设计的,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查行级锁 、页面锁、乐观锁与悲观锁上述对MySQL两种较为常见的锁粒度进行了阐述(共享锁与排他锁,全局锁、表级锁、行级锁),接着再来看看页面锁、乐观锁与悲观锁6.1 页面锁页面锁是Berkeley DB存储引擎支持的一种锁粒度 因此最终加锁的范围是(5,10);所以session B往这个间隙里面插入id=8的记录会被锁住,但是session C修改id=10这是可以的等值查询上MySQL的优化索引上的等值查询,如果是唯一索引
分布式锁更合理的用法是: 手动加锁 业务操作 手动释放锁 如果手动释放锁失败了,则达到超时时间,redis会自动释放锁。 大致流程图如下: 那么问题来了,如何释放锁呢? 但还记得前面我们给锁设置过超时时间吗?即使出现异常情况造成释放锁失败,但到了我们设定的超时时间,锁还是会被redis自动释放。 但只在finally中释放锁,就够了吗? 如果减1后,重入锁的value值还等于0,则可以删除锁,然后发消息通知等待线程抢锁。 下面总结一个读写锁的特点: 读与读是共享的,不互斥 读与写互斥 写与写互斥 6.2 锁分段 此外,为了减小锁的粒度,比较常见的做法是将大锁:分段。 8 主从复制的问题 上面花了这么多篇幅介绍的内容,对单个redis实例是没有问题的。 but,如果redis存在多个实例。
分布式锁更合理的用法是: 手动加锁 业务操作 手动释放锁 如果手动释放锁失败了,则达到超时时间,redis会自动释放锁。 大致流程图如下: 那么问题来了,如何释放锁呢? 但还记得前面我们给锁设置过超时时间吗?即使出现异常情况造成释放锁失败,但到了我们设定的超时时间,锁还是会被redis自动释放。 但只在finally中释放锁,就够了吗? 如果减1后,重入锁的value值还等于0,则可以删除锁,然后发消息通知等待线程抢锁。 下面总结一个读写锁的特点: 读与读是共享的,不互斥 读与写互斥 写与写互斥 6.2 锁分段 此外,为了减小锁的粒度,比较常见的做法是将大锁:分段。 8 主从复制的问题 上面花了这么多篇幅介绍的内容,对单个redis实例是没有问题的。 but,如果redis存在多个实例。