锁相关机制-要求对事务比较了解,有一定的锁概念基础

一:加锁的意义/为什么要加锁

锁应用于多线程并发的场景,用于保证数据的一致性,如果只有单线程那上锁就没有意义.常见的场景就是:电商的库存管理,用来防止库存超发,总库存和sku的库存对不上等问题

二:常说的锁有哪些

数据库:写锁&读锁,行锁&表锁
java:线程锁
概念:乐观锁&悲观锁,独占锁/互斥锁/排他锁&共享锁,公平锁&非公平锁,自旋锁

2.1:读锁&写锁

说到数据库锁就不得不说一下数据库的存储引擎,常见的有MyISAM、InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDBCluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等
最常用的有:MyISAM和InnoDB两种,锁的机制也是基于这两种引擎,版本5.5.x
以前是默认的MyISAM,是不支持事务的.5.5.x版本后开始默认使用InnoDB

读锁: --悲观锁 --共享锁
操作(一致):
	LOCK TABLE goods READ;		加锁
	......
	UNLOCK TABLES;				释放锁

MyISAM(不支持事务):	
	加锁后包含本线程在内的所有线程都只能进行读操作,MyISAM的读取默认就是加读锁
InnoDB:	
	如果当前线程给当前表加上了读锁,那么其他线程就不能再给当前表加别的锁(也只能加读锁),
	当前线程只可以对当前表进行读操作,进行写操作会直接报错.
	其他线程也可以对当前表进行读操作,进行写操作会直接阻塞,待当前线程锁释放后写入成功.
写锁: --悲观锁 --排他锁
LOCK TABLE goods WRITE;		加锁
......
show open tables;			查询加锁的表,1为加锁
UNLOCK TABLES;				释放锁

MyISAM(不支持事务):	
	加锁后本线程只能够对此表进行读,写操作,如果当前操作没有完成就无法进行别的操作.
	其他线程无法进行任何操作.
InnoDB:	
	如果当前线程给当前表加上了写锁,那么其他线程就不能再给当前表加任何锁.
	当前线程对该表只能进行写操作.读操作会进入阻塞,直到当前线程释放锁,
	其他线程的任何操作都混进入阻塞,直到当前线程释放锁.
读锁&写锁的对比:
总体来说都是进行了锁表,并发量不大的项目中可以使用.
在多线程高并发的情景下不推荐使用(会严重的影响服务器的性能和吞吐量).
2.2:行锁&表锁
行锁: --悲观锁 --排他锁
SET autocommit = 0;								//开启事务
SELECT * FROM goods WHERE id = 13 FOR UPDATE;	//加锁查询
commit;											//提交事务

实现行锁的基本条件是:
1.必须要有事务
2.必须在SELECT语句后面加上FOR UPDATE
3.必须索引有效(InnoDB的行锁是针对SELECT...WHERE字段的索引加的锁,如果没有索引或者索引失效会导致行锁升级为表锁)
表锁: --悲观锁 --读锁&写锁
上面提到的读锁和写锁就是表锁,直接锁定整张表
行锁&表锁的对比:
表锁的优点很明显:开销小,实现逻辑简单,加锁和释放锁都很快,无死锁.
表锁的缺点也很明显,上面也提到过:范围太大,容易发生锁冲突,并发低.
行锁的优点:数据库支持的最小粒度的锁,一次性对一条数据加锁,不易发生锁冲突,并发度高
行锁的缺点:开销比较大,加锁也比较慢,还有可能出现死锁(可能导致系统奔溃),在使用的时候一定要注意
2.3:线程锁
线程锁:
说到线程锁就不得不提JAVA中的@Synchronized:--独占锁 --悲观锁 --非公平锁
在JDK1.6以前Synchronized每次加锁没需要依赖操作系统的Mutex Lock实现,
线程需要切换到内核态,切换成本很高,是重量级锁,并不推荐使用
在JDK1.6之后Synchronized增加了四种锁态:无锁状态,偏向锁状态,轻量级锁状
态,重量级锁状态,会随着竞争逐渐升级

据说是因为sum公司的程序员发现大部分程序大多数时间不会发生多个线程同
时访问静态资源的情况,所以使用了这种策略.可以提高获得锁和释放锁的效率.

偏向锁: 只有一个线程进入临界区
轻量级锁: 多个线程交替进入临界区
重量级锁: 多个线程同时进入临界区

synchronized在线程竞争锁时,首先做的不是直接进入contentionlist队列
排队,在轻量级锁时会尝试自旋获取锁,如果获取不到升级为重量级锁进入
contentionlist队列,这明显对于已经进入队列的线程是不公平的(不公平锁)

在JAVA中除了常见的Synchronized之外还有ReentrantLock,Semaphore,AtomicInteger等
每种锁机制都有各自的适用场景,这里就不一一赘述了,感兴趣的小伙伴可以自行了解
2.4:乐观锁&悲观锁
乐观锁:
每次获取数据的时候都认为别人不会修改,所以不会上锁.但是在最后跟新的时候会去
校验在获取数组到改动期间有没有去更新该数据,常见的使用就是版本号(version)机制
悲观锁:
每次去拿数据的时候都认为别人会修改,在拿数据的时候就会直接上锁,这样其他人操
作该数据时就会进入阻塞

上面的行锁,表锁,读锁,写锁,Synchronized都是典型的悲观锁
乐观锁&悲观锁的对比:
乐观锁也叫做无锁,不加锁的特点能大大提升读操作的性能
悲观锁更适用于写操作多的场景,先加锁可以保证数据的正确性
2.5:独占锁/互斥锁/排他锁&共享锁
独占锁/互斥锁/排他锁(Exclusivelocks简称为X锁):
指的是该锁一次只能被一个线程获得,例如:Synchronized,写锁等

关于这个叫法,因为所处的层面不同(数据库层,程序层等),创建的程序员不同等因素,
就大概分成了这么几种叫法,其实是一个意思.常说的数据库X锁是排它锁,咋叫都行...
共享锁(Share locks简称为S锁):
指的是该锁可以被多个线程锁持有,例如:读锁
独占锁/互斥锁/排他锁&共享锁的对比:
两个都是通过AQS来实现的,根据实现不同的方法,来实现是共享还是独享.
如读锁的共享锁可保证并发度是非常高效的,读写,写读,写写的过程是互斥的
如写锁的排它锁只有自己能持有锁,消耗性能但是是绝对安全
2.6:公平锁&非公平锁
公平锁:
加锁前先查看是否前面已经有排队等待的线程,有的话优先处理排在前面的线程,类似队列
非公平锁:
加锁前先尝试获取锁,获取不到进入队列尾等待
公平锁&非公平锁的对比:
非公平锁相对公平锁性能会高5-10倍,因为公平锁需要在多核情况下维护一个队列,
如果当前线程不是当前队列的第一个,既增加了队列长度,又增加了队列线程的切换数
2.7:自旋锁
自旋锁与互斥锁有点类似,区别就是互斥锁在拿不到锁的时候线程是睡眠的(阻塞),
而自旋锁是保持循环获取锁的这么一种状态.因此才成为"自旋"
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值