有分布式锁为什么还要使用本地锁
现在绝大多数的系统都采用了分布式架构,不管是内置锁synchronized还是显示锁Lock都无法跨jvm满足分布式要求,通常是使用redis等实现分布式锁来解决线程安全问题,那jdk为我们提供的锁已经无用武之地了吗?并不是,不管采用哪种方式实现的分布式锁(如数据库,redis、memcached,zookeeper)都涉及网络io开销,在需要频繁加锁释放锁的时候性能肯定远不如在本地内存中直接操作。所以更优的做法应该是先抢本地锁,持有本地锁的线程再去竞争分布式锁。
显示锁ReentrantLock的适用场景
jdk为我们提供了synchronized关键字(内置锁)和Lock接口两种锁,Lock接口默认实现为ReentrantLock,什么情况下应该使用ReentrantLock,我们可以看一下它的以下三个方法
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock的有参构造,根据接收的布尔类型的参数决定实例化一个公平锁或非公平锁。而synchronized是非公平锁。
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
此方法为“可中断的获取锁”,它提高了显式锁的灵活性,在使用synchronized同步代码块时,一旦开始拿锁准备进入同步代码块,线程就会一直处于等待锁的状态而无法响应中断。而显示锁中提供的这个方法,可以实现在等待锁时,允许其他线程调用当前线程的interrupt方法来中断当前线程的等待。
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
和synchronized不同,显示锁的tryLock方法可以实现“尝试获取锁”,意思是竞争一次锁,拿到了则返回true,未拿到返回false,而不是一直等待其他线程释放锁当前线程拿到锁为止。
总结:
根据显示锁以上三个方法的特性可得出显示锁的适用场景
1.对锁的公平性有硬性要求
2.在等待锁时需要响应中断
3.需要用到“尝试获取锁”的场景,而不仅仅是阻塞式的获取锁
除此之外的场景,在jdk1.6之后都应该尽量使用synchronized,因为jdk1.6为synchronized锁做了大量优化(jdk1.6对synchronized做了哪些优化?),此外,如果是读多写少的场景,可以使用读写锁ReentrantReadWriteLock(ReentrantReadWriteLock简介)。