Atitit.软件与编程语言中的锁机制原理attilax总结
1. 用途 (Db,业务数据加锁,并发操作加锁。 2
2. 锁得类型 3
2.1. 排它锁 “互斥锁 共享锁 乐观锁与悲观锁 乐观锁: 3
2.2. 自旋锁还是信号量 3
2.3. -自动释放还是手动释放 3
2.4. 按照使用者 数据库锁 操作系统锁 3
2.5. 按照外观 u型锁 一字锁 月牙锁 Synchronized Monitor类 3
2.6. 按照工作原理 弹簧锁 弹子锁 3
2.7. 实现方式,语言方式与库方式 3
2.8. 行级锁 表级别锁 根据锁的粒度来区分 3
2.9. 表级锁:5种 共享锁(SHARE) 排他(EXCLUSIVE) 悲观锁: 乐观锁: 4
2.10. PS:只有共享锁与共享锁相互兼容,共享锁与排它锁、排它锁之间都互不兼容 7
2.11. 按照数据库维度分类 共享锁与排它锁 更新锁 7
2.12. 按照思想维度分类 悲观锁 乐观锁 7
3. 锁机制 提供的锁类型以及加解锁方法 8
3.1. Java的锁机制 Synchronized ReentrantLock AtomicInteger 8
3.2. 使用 JDK1.5 提供的读写锁(java.util.concurrent.locks.ReadWriteLock) 8
3.3. 4、使用 JDK1.5 提供的 java.util.concurrent.ConcurrentHashMap 类 9
3.4. C# 锁原理(Monitor类和lock关键词 ReaderWriterLock 9
4. 锁的实现机制 大多是基于数据版本versin记录机制 9
4.1.1. 乐观锁另一种实现方式CAS 10
4.2. test and set”指令 10
4.3. 锁内存总线 10
4.4. 硬件层面,CPU提供了原子操作、关中断、锁内存总线的机制 10
5. 加锁与解锁机制 11
5.1. 就像停车场出门被堵住,首先和对方协商 11
5.2. 找不到对方,或者联系不上,然后找管理者拆除 11
5.3. 如果找不到管理者,自我拆除,或者退回等待 11
6. 不同数据库锁机制 mysql mssql 11
7. 死锁 12
7.1. 1、系统出现死锁必然同时保持的四个必要条件: 12
7.2. 2、死锁的 防止策略 : 12
7.3. 三、死锁的避免( 简单应用 ) 13
7.4. 3、银行算法是怎样避免死锁的: 13
7.5. 四、死锁的检测( 领会 ) 死锁的检测 14
7.6. 分布式锁机制 15
8. Ref参考资料 15
8.1. 深入浅出Java并发包—锁机制(一) - 一线天色 天宇星辰的日志 - 网易博客.html 16
8.2. 【Java线程】锁机制:synchronized、Lock、Condition - Alpha's 学习笔记 - 博客频道 - CSDN.NET.html 16
8.3. Java常用锁机制简介 - hduhans - 博客园.html 16
8.4. 5天不再惧怕多线程——第二天 锁机制 - 一线码农 - 博客园.html 16
8.5. C# 多线程编程之锁的使用【互斥锁(lock)和读写锁(ReadWriteLock)】 - C#编程语言程序开发技术文章_C#编程 - 红黑联盟.html 16
8.6. 锁机制与原子操作 _第四篇_ - 逆心 - 博客园.html 16
8.7. 简单的JavaScript互斥锁.html 16
8.8. Atitit。Cas机制 软件开发 编程语言 无锁机制 java c# php.docx 16
8.9. atititi.同步锁机制 16
8.10. 数据库锁机制 - Tobi7 - 博客园.mhtml 16
8.11. Atitit.线程死锁的解析and调试.docx 16
8.12. atitit.sql server2008 查询死锁and 解决.doc 17
8.13. Atitit gc资源释放器死锁解除器 适合于游戏引擎,数据库释放 爬虫网络连接释放等.docx 17
8.14. atitit 数据库死锁处理总结.txt 17
8.15. atitit 线程死锁in cmd调用的解决方案.docx.txt 17
8.16. atitit 软件运行与资源占用可靠性原理(cpu,资源泄露,死锁,崩溃的解决).docx.txt 17
8.17. atitit.sql server2008 查询死锁and 解决.doc 17
8.18. atitit.减少数据库死锁总结o91 fx.doc 17
8.19. atitit.提升稳定性---hb 死锁 jsp seselvet 多线程解决.txt 17
8.20. atitit.线程死锁in cmd调用的解决方案.docx 17
8.21. Atitit.线程死锁的解析and调试.docx 17
8.22. atitit。修改字段死锁mssql.doc 17
8.23. atitti.测试支持----生成死锁的表格.doc 17
Java.util.concurrent.lock 中的Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。
Reen
行级锁。特点:开锁大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。适合于有大量按索引更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理系统。
行共享 (ROW SHARE) – 禁止排他锁定表,与行排他类似,区别是别的事务还可以在此表上加任何排他锁。(除排他(exclusive)外)
行排他(ROW EXCLUSIVE) – 禁止使用排他锁和共享锁,其他事务依然可以并发地对相同数据表执行查询,插入,更新,删除操作,或对表内数据行加锁的操作,但不能有其他的排他锁(自身是可以的,没发现有什么用)
共享锁(SHARE) - 锁定表,对记录只读不写,多个用户可以同时在同一个表上应用此锁,在表没有被任何DML操作时,多个事务都可加锁,但只有在仅一个事务加锁的情况下只有此事务才能对表更新;当表已经被更新或者指定要更新时(select for update),任何事务都不能加此锁了。
共享行排他(SHARE ROW EXCLUSIVE) – 比共享锁更多的限制,禁止使用共享锁及更高的锁,在表没有被任何DML操作时,只有一个事务可以加锁,可以更新,书上说别的事务可以使用select for update锁定选中的数据行,可是实验后没被验证。
排他(EXCLUSIVE) – 限制最强的表锁,仅允许其他用户查询该表的行。禁止修改和锁定表
行级锁和表级锁是根据锁的粒度来区分的,行记录,表都是资源,锁是作用在这些资源上的。如果粒度比较小(比如行级锁),可以增加系统的并发量但需要较大的系统开销,会影响到性能,出现死锁,,因为粒度小则操作的锁的数量会增加;如果作用在表上,粒度大,开销小,维护的锁少,不会出现死锁,但是并发是相当昂贵的,因为锁定了整个表就限制了其它事务对这个表中其他记录的访问。
悲观锁:
Pessimistic Lock正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守悲观态度,事务每次去操作数据的时候都假设有其他事务会修改需要访问的数据,所以在访问之前都要求上锁,行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能 真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系 统不会修改数据)。 一个典型的倚赖数据库的悲观锁调用: select * from account where name=”Erica” for update 这条sql 语句锁定了account 表中所有符合检索条件(name=”Erica”)的记录。 本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。
Hibernate悲欢锁实现:基于数据库锁机制
Query q=Session.createQuery("select * from t_profit where amount>10000");
q.setLockMode("Profit",LockMode.UPGRADE);//Profit是Profit类的别名
List
执行的sql:select ....from t_profit where amount>10000 for update.hibernate的悲观锁通过数据库的for update实现。
LockMode.NONE:无锁机制;
LockMode.WRITE:insert,update记录时自动获取悲观锁;
LockMode.READ在读取时自动获取悲观锁;
LockMode.UPGRADE:利用数据库的for update子句加锁;
LockMode.UPGRADE_NOWAIT:oracle特定实现,用oracle的for update nowait子句加锁
乐观锁:
Optimistic Lock,和悲欢锁相反,事务每次去操作数据之前,都假设其他事务不会修改这些需要访问的数据 ,所以 在访问之前不要求上锁,只是在进行更新修改操作的时候判断一下在访问的期间有没有其他人修改数据 了。它适用于多读的应用类型,冲突真的发生比较少的时候就比较好,这样省去了开销的开销,可以提高吞吐量;但如果是真的经常要发生冲突的,那每次还要去判断进行retry,反倒降低的性能,这个时候悲欢锁比较好。数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
它的实现大多是基于数据版本versin记录机制。举个例子:
1.利润表t_profit中有一个 version字段,当前值为1;而总资产余额字段(balance)为$10000
2.操作员A读出version=1,从总资产减除2000,10000-2000=8000.
3.A还没操作结束,此时操作员B也读出version=1,总资产减除5000,10000-5000=5000.
4.A操作完成,把version加1,修改为2,把总资产减2000后提交更新数据库,更新成功
5.B操作了,也加version加1,修改为2,把总资产减5000后提交更新数据库,此时发现version已经为2了,如B修改后加1的version一样,不满足乐观锁策略:"提交的版本必有大于记录当前的版本才能执行"。因此B的操作请求被驳回,这样就避免了B就version=1的旧数据修改的结果覆盖了A操作的结果的可能。如没有乐观锁,那A减去2000后剩余8000,但B操作的时候是用10000-5000剩余5000的,如果B的提交成功,总资产余额就是5000,但实际情况应该是8000-5000=3000的。出现总资产表记录和实际支出不一致。
Hibernate对乐观锁的实现:
· 共享锁:用于不更改或不更新数据的操作(只读操作)。共享锁允许并发事务读取同一个资源,数据资源上存在共享锁时,任何其他事务不允许修改数据
· 排它锁: 用于数据修改,确保不会同时多重更新同一数据。资源上存在排他锁时,其他任何事务不允许给资源上锁,当资源上有其他锁时,也无法对其加上排它锁
· 更新锁
· 悲观锁
1. 悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。悲观锁的实现,往往依靠数据库提供的锁机制。悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。
2. 悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数
· 乐观锁
1. 乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。
2. 乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会主动产生任何锁和死锁。但是在并发量高的情况下,可能导致某次数据修改多次重试,影响单次成功操作的时间。
3. 数据版本实现乐观锁:实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。
是基于JVM来保证数据同步的,而Lock则是在硬件层面,依赖特殊的CPU指令实现数据同步的
实际JDK中也是通过一个32bit的整数位进行CAS操作来实现的。
需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内!!
Atomic* 开头的类基本原理都是一致的, 都是借助了底层硬件级别的 Lock 来实现原子操作的。 Cas原理
3、。代码如下
rwlock.readLock().lock();
value = map.get(key);
rwlock.readLock().unlock();
这样两个读操作可以同时进行,理论上效率会比方法 2 高。
。该类将 Map 的存储空间分为若干块,每块拥有自己的锁,大大减少了多个线程争夺同一个锁的情况。代码如下
value = map.get(key); //同步机制内置在 get 方法中
C#提供了2种手工控制的锁
一: Monitor类
这个算是实现锁机制的纯正类,在锁定的临界区中只允许让一个线程访问,其他线程排队等待。主要整理为2组方法。
1:Monitor.Enter和Monitor.Exit
微软很照护我们,给了我们语法糖Lock,对的
二:ReaderWriterLock类
先前也知道,Monitor实现的是在读写两种情况的临界区中只可以让一个线程访问,那么如果业务中存在”读取密集型“操作,就
好比数据库一样,读取的操作永远比写入的操作多。针对这种情况,我们使用Monitor的话很吃亏,不过没关系,ReadWriterLock
就很牛X,因为实现了”写入串行“,”读取并行“。
它的实现大多是基于数据版本versin记录机制。举个例子:
1.利润表t_profit中有一个 version字段,当前值为1;而总资产余额字段(balance)为$10000
2.操作员A读出version=1,从总资产减除2000,10000-2000=8000.
3.A还没操作结束,此时操作员B也读出version=1,总资产减除5000,10000-5000=5000.
4.A操作完成,把version加1,修改为2,把总资产减2000后提交更新数据库,更新成功
5.B操作了,也加version加1,修改为2,把总资产减5000后提交更新数据库,此时发现version已经为2了,如B修改后加1的version一样,不满足乐观锁策略:"提交的版本必有大于记录当前的版本才能执行"。因此B的操作请求被驳回,这样就避免了B就version=1的旧数据修改的结果覆盖了A操作的结果的可能。如没有乐观锁,那A减去2000后剩余8000,但B操作的时候是用10000-5000剩余5000的,如果B的提交成功,总资产余额就是5000,但实际情况应该是8000-5000=3000的。出现总资产表记录和实际支出不一致。
Hibernate对乐观锁的实现:
CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
CAS 操作包含三个操作数 —— 内存位置(V
DBMS |
SELECT |
UPDATE |
INSERT |
DELETE |
MySQL(InnoDB) |
不加锁 |
排它锁 |
排它锁 |
排它锁 |
SQL SERVER |
共享锁 |
更新锁 |
排它锁 |
排它锁 |
1)互斥使用资源
2)占有并等待资源
3)不可抢夺资源
4)循环等待资源
要防止死锁形成,只要采用的资源分配策略能使上述4个条件中有一个条件不成立就可以了。
1)破坏互斥使用资源的条件经常是行不通的。因为资源本身特性就是互斥使用的。
2)要破坏“占有并等待条件”则可以采取两种办法: 静态分配 和 释放已占资源 .
静态分配 也称为 预分配资源 ,要求每一个进程在开始执行前就申请它所需要的全部资源,仅当系统能满足进程的资源申请要求且把资源分配给进程后,该进程才能开始执行。
释放已占资源 就是指进程申请资源时必须没有占用资源,如果已经占用了资源就要先归还所占的资源再申请。
3)实现 可抢夺式分配 :如果一个进程已经占有了某些资源又要申请新资源,而新资源不能满足(已被其它进程占用)必须等待时,系统可以抢夺该进程已占有的资源。
4)实现 按序分配 :把系统中所有资源排一个顺序,对每一个资源给一个确定的编号,规定任何一个进程申请两个以上的资源时,总是先申请编号小的资源,再申请编号大的资源。
死锁的避免不同于死锁的防止,死锁的防止是采用某种分配策略后,系统就不会产生死锁,这好比是你打过了某种预防针,再也不会得那种病。而死锁的避免是没有打预防针,但是通过其他办法,避免得病。因此有“安全状态”的说法,对应的,当然也有不安全状态。就像人都有得病的可能,不必任何病都打预防针。只要注意防病,仍然可以安全健康的生活。
1、 安全状态 :如果操作系统能保证所有的进程在 有限的时间 内得到需要的 全部资源 ,则称系统处于“安全状态”。
2、区分死锁的 避免 与死锁的 防止 :当采用了防止死锁的资源分配策略后,系统中就不会形成死锁。但是可以防止死锁的资源分配策略中,有的只适用于对某些资源的分配,有的会影响资源的使用效率。这时可用使用死锁的避免。
死锁的避免是解决死锁的另一种方法,它不同于死锁的防止。在系统中不采用防止死锁的资源分配策略,而是估计到可能有死锁发生时避免死锁的发生。
银行家算法是这样的:
1)当一个用户对资金的最大的需求量不超过银行家现有的资金时就可以接纳该用户。
2)用户可以分期贷款,但贷款的总数不能超过最大需求量。
3)当银行家现有的资金不能满足用户的尚需贷款时,对用户的贷款可推迟支付,但总能使用户在有限的时间里得到贷款。
4)当用户得到所需的全部资金后,一定能在有限的时间里归还所有资金。
我们把操作系统看作是银行家,操作系统管理的资源相当于是银行家管理的资金,则银行家算法就是:
1)当一个进程首次申请资源时,测试该进程对资源的最大的需求量,如果不超过系统现存资源时就可以按他的当前申请量为其分配资源。 否则推迟分配。
2)进程执行中继续申请资源时,测试该进程占用资源和本次申请资源总数有没有超过最大需求量。超过就不分配,没超过则再测试现存资源是否满足进程还需要的最大资源量,满足则按当前申请量分配,否则也推迟分配。
总之,银行家算法要保证分配资源时系统现存资源一定能满足至少一个进程所需的全部资源。这样就可以保证所有进程都能在有限时间内得到需要的全部资源。这就是安全状态。
(银行家算法在操作系统的实践考试中可能会用到)
就是既不打预防针,也不去避免得病,而是经常去体检,如果发现有病了就治疗。这是一种事后解决的办法,也算是解决死锁问题的一条途径。但这毕竟要付出较大代价。
1、什么是 死锁的检测 :对资源的申请和分配不加限制,只要有剩余的资源就可把资源分配给申请者。这样可能会出现死锁,系统定时运行一个“死锁检测程序”,如果检测到死锁发生,则必须先解除死锁再继续工作。
2、怎样实现死锁的检测:1、每个资源当用中只有一个资源2、资源类中含有若干个资源。
3、 死锁的解除 :一般采用两种方式来解除死锁,一种是终止一个或几个进程的执行以破坏循环等待;另一种是从涉及死锁的进程中抢夺资源。
检测死锁和解除死锁都要付出很大代价。所以用死锁检测的方法解决死锁问题只适用于 不经常发生死锁 的系统中。
窗体顶端
窗体底端
·
· a408308321
· 2016年09月06日 18:07
· 775
前言: 由于在平时的工作中,线上服务器是分
作者:: 绰号:老哇的爪子 ( 全名::Attilax Akbar Al Rapanui 阿提拉克斯 阿克巴 阿尔 拉帕努伊 )
汉字名:艾提拉(艾龙), EMAIL:[email protected]
转载请注明来源: http://blog.csdn.net/attilax
atiend