常见的各种锁

十二月 18, 2022 [lock] #lock

常见的各种锁

常见的各种锁

1、悲观锁 悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,在获取数据的时候先加锁,确保数据的安全性。 锁实现:关键字synchronized、Lock接口的实现 使用场景:写操作比较多,先加锁可以保证写操作时数据正确 2、乐观锁 乐观锁认为自己在使用数据的时候不会被别的线程修改,所以不会添加锁,只是在更新的时候去判断之前有没有别的线程更改过这个数据 锁实现:CAS算法,例如AtomicInteger类的原子自增底层是通过CAS实现的 使用场景:读多,不加锁的特点能够使读的性能大幅度提升

3、读锁(共享锁) 读锁即共享锁(S锁):共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。 4、写锁(排他锁) 写锁即排他锁(X锁):用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。 5、行锁 行锁即对数据表中每一行数据加锁,数据库最细粒度的锁,开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高 实现:InnoDB

6、表锁 表锁即对数据库中每个表加锁,数据库中最大级别的锁,开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低 实现:MyISAM、BDB、InnoDB 7、页锁 页锁即对组加锁,对相邻数据加锁,数据库中介于表锁和行锁之间的锁,开销和加锁速度介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般 实现:BDB

8、互斥锁(重量级锁或阻塞同步、悲观锁) 互斥锁是一个互斥的同步对象,意味着同一时间有且仅有一个线程可以获取它,互斥锁可适用于一个共享资源每次只能被一个线程访问的情况 9、自旋锁(CAS) 自旋锁在申请资源但是申请不到的情况下并不会挂起,而是会选择持续申请。这种锁结果适用于每个线程占用较少时间的锁,并且线程阻塞状态切换的代价远高于等待的代价时使用。 10、分布式锁 在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。 实现:数据库实现分布式锁; 缓存(Redis等)实现分布式锁; Zookeeper实现分布式锁; 11、区间锁(分段锁) ConcurrentHashMap jdk1.7使用了分段锁来保证线程安全,效率比起使用synchronized的HashTable要高的很多。每个集合都可以看作是一个存储东西的房子,HashTable与ConcurrentHashMap存储的都是HashEntry数组(每个数组里面是链表,暂且忽略,直到就好) 12、重入锁 重入锁当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的 ,可避免死锁 锁实现:关键字synchronized,ReentrantLock锁实现 13、非重入锁 非重入锁与可重入锁相反,不可递归调用,递归调用就发生死锁。 锁实现:NonReentrantLockk锁实现 14、公平锁 公平锁多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。 锁实现:ReentrantLock(true)锁实现 优点:所有的线程都能得到资源,不会饿死在队列中。 缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。 15、非公平锁 非公平锁多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。 缺点:1、你们可能也发现了,这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死2、会发生羊群效应