文章目录
- 一.概念
- 二.常见锁及其应用场景
- 1. synchronized关键字
- 1.1 应用场景
- 1.2 特点
- 2. ReentrantLock(可重入锁)
- 2.1 应用场景
- 2.2 特点
- 3. ReadWriteLock(读写锁)
- 3.1 应用场景
- 3.2 特点
- 4. OptimisticLock(乐观锁)
- 4.1 应用场景
- 4.2 特点
- 5. PessimisticLock(悲观锁)
- 5.1 应用场景
- 5.2 特点
- 6. CountDownLatch(倒计时锁存器)
- 6.1 应用场景
- 6.2 特点
一.概念
- 在Java中,锁是一种同步机制,用于控制多线程对共享资源的访问,以确保在任一时刻,只有一个线程能够执行业务代码。
- 锁的主要目的是防止多个线程同时修改共享资源,从而保证数据的一致性和线程安全。
二.常见锁及其应用场景
1. synchronized关键字
1.1 应用场景
- 用于方法或代码块上,保证同一时间只有一个线程能够执行当前代码。
- 每个对象都有一个监视器(monitor),当一个线程访问被synchronized修饰的方法或者代码块时,它必须先获得该对象的监视器.
1.2 特点
- 锁的粒度比较大,只能锁定整个方法或代码块.属于重量级锁.
- 可重入锁:一个线程获取了锁之后,可以再次获取这个锁而不会被阻塞,通常用于复杂的业务逻辑.
- 不可中断:当一个线程持有synchronized锁时,其他线程无法中断它,除非抛出异常或者正常执行结束。
2. ReentrantLock(可重入锁)
2.1 应用场景
需要更灵活的锁操作时使用,比如尝试非阻塞获取锁、可中断获取锁、公平性选择、超时获取锁。
2.2 特点
-
需要显示地获取和释放
-
可中断:当一个线程持有ReentrantLock锁时,其他线程可中断它的执行–lockInterruptibly()方法
-
提供了公平锁和非公平锁的选项,可以通过构造函数指定.
-
尝试非阻塞获取锁–tryLock()方法-线程在尝试获取锁时,如果锁不可用,线程不会进入等待状态,而是立即返回一个失败的结果.从而避免线程在等待锁的过程中被长时间阻塞,比如热点key过期,未获取锁的线程可以返回给用户历史数据,而不是阻塞在那里等待获取锁,为的就是提升用户体验.
-
超时获取:允许在指定时间内尝试获取锁—tryLock(long timeout, TimeUnit unit)方法–如果获取成功,则执行业务逻辑;如果超时未能获取锁,则执行其他操作或重试.
3. ReadWriteLock(读写锁)
3.1 应用场景
当读操作远多于写操作时,用于提高并发性能,允许多个读操作同时进行,写操作则独自占用锁.
3.2 特点
由两个锁组成,一个读锁.一个写锁,读锁可以被多个读操作共享,写锁是独占的.
4. OptimisticLock(乐观锁)
4.1 应用场景
一般用于读多写少的场景
4.2 特点
乐观地认为共享数据被修改的概率很低,所以在读取的时候不会加锁.一般通过版本号或者时间戳来实现.线程在提交更新的时候会检查数据是否被其他线程修改过.
5. PessimisticLock(悲观锁)
5.1 应用场景
一般用于写多读少的场景
5.2 特点
悲观地认为共享数据被修改的概率很高,因此在数据被访问的时候立即加锁,以防止其他线程的修改.
6. CountDownLatch(倒计时锁存器)
6.1 应用场景
用于一个或多个线程等待其他线程完成操作后再继续执行的场景,比如主线程阻塞,等所有子线程操作完之后,再继续执行。
6.2 特点
通过一个计数器来控制,当计数器减到0时,所有等待的线程才会继续执行.