ReentrantLock
Java 除了使用关键字 Synchronized 外,还可以使用 ReentrantLock 实现独占锁的功能。而且 ReentrantLock 相比 Synchronized 而言功能更加丰富,使用起来更为了灵活,也更适用于复杂的并发场景。
1. 简介
ReentrantLock 常常对比着 Synchronized 来分析
1)Synchronized 是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock 也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活
2)Synchronized 可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁,ReentrantLock 也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁
3)Synchronized 不可响应中断,一个线程获取不到锁就一直等着,ReentrantLock 可以响应中断
从上面看 ReentrantLock 比 Synchronized 没好太多,但是,Synchronized 所没有的一个最主要的就是 ReentrantLock 还可以实现公平锁机制。公平锁就是在锁上等待时间最长的线程获得锁的使用权。
2. 使用
1)简单使用
public class MyReenterLock { public static final Lock lock = new ReentrantLock(true); public static void test(){ try{ lock.lock(); System.out.println(Thread.currentThread().getName() + " 获取了锁 "); TimeUnit.SECONDS.sleep(2); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); } } public static void main(String[] args) { new Thread(() -> test(), "线程A").start(); new Thread(() -> test(), "线程B").start(); } }
2)公平锁
public static final Lock lock = new ReentrantLock(true);
3)非公平锁
public static final Lock lock = new ReentrantLock(false);
4)响应中断
响应中断就是一个线程获取不到锁,不会傻傻的等下去,ReentrantLock 会给予一个中断回应。
public class MyReentrantLock2 { static Lock lock1 = new ReentrantLock(); static Lock lock2 = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new ThreadDemo(lock1, lock2)); Thread t2 = new Thread(new ThreadDemo(lock2, lock1)); t1.start(); t2.start(); t2.interrupt(); } } class ThreadDemo implements Runnable{ Lock firstLock; Lock secondLock; public ThreadDemo(Lock firstLock, Lock secondLock){ this.firstLock = firstLock; this.secondLock = secondLock; } @Override public void run() { try{ firstLock.lockInterruptibly(); TimeUnit.SECONDS.sleep(1); secondLock.lockInterruptibly(); }catch (InterruptedException e){ e.printStackTrace(); }finally { firstLock.unlock(); secondLock.unlock(); System.out.println(Thread.currentThread().getName() + " 获取到资源,正常结束!"); } } }
5)限时等待
通过 tryLock 方法,可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true 表示获取锁成功,false 表示获取锁失败。
if(!firstLock.tryLock()){TimeUnit.SECONDS.sleep(1);}
我们也可以设置 tryLock 的超时等待时间 try(long timeout, TimeUnit unit),也就是说一个线程在指定的时间内没有获取到锁,就会返回 false,就可以做其他事情了。
3. 应用场景
场景一:如果发现该操作已经在执行中,则不再执行(有状态执行)
1)用在定时任务时,如果任务执行时间可能超过下次计划执行时间,确保该有状态任务只有一个正在执行,忽略重复触发。
2)用在界面交互时点击执行较长时间请求操作时,防止多次点击导致后台重复执行(忽略重复触发)
这两种情况多用于进行非重要任务防止重复执行
if(lock.tryLock()){ ..... lock.unlock();}
场景二:如果发现该操作已经在执行,等待一个一个执行(同步执行,类似 Synchronized)
这种比较常见,主要是防止资源使用冲突,保证同一时间内只有一个操作可以使用该资源。但与 Synchronized 的明显区别是性能优势(伴随 JVM 优化这个差距在减小)。同时 Lock 有更灵活的锁定方式,而 Synchronized 永远是公平的。
这种情况主要用于对资源的争抢(如:文件操作,同步消息发送,有状态的操作等)
lock.lock(); // 如果被其他资源锁定,会在此等待锁释放,达到暂停的效果
场景三:如果发现该操作已经在执行,则尝试等待一段时间,等待超时则不执行
// 如果已经被lock,则尝试等待 5s,看是否可以获得锁
if(lock.tryLock(5, TimeUnit.SECONDS))
场景四:如果发现该操作已经在执行,等待执行。这时可中断正在进行的操作立刻释放锁继续下一操作
Synchronized 与 Lock 在默认情况下不会响应中断操作,会继续执行完。lockInterruptibly() 提供可中断锁来解决此问题。
这种情况主要用于取消某些操作对资源的占用。
https://baijiahao.baidu.com/s?id=1648624077736116382&wfr=spider&for=pc
https://blog.csdn.net/antony9118/article/details/52664125?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control