锁降级 : 是指保持住当前的写锁(已拥有),再获取读锁,随后释放写锁的过程。
1. 锁降级的用途
锁分为读锁 (共享锁)、写锁(排他锁)两种:
- 一个线程获取了写锁,其他线程无法获取写锁、读锁,进行阻塞;
- 一个线程获取了读锁,其他线程无法获取写锁(进行阻塞),但是可以获取读锁;
如果只使用写锁,那么释放写锁之后,其他线程就会获取到写锁或读锁,使用锁降级可以在释放写锁前获取读锁,这样其他的线程就只能获取读锁,对这个数据进行读取,但是不能获取写锁进行修改,只有当前线程释放了读锁之后才可以进行修改。
这样有什么好处:
- 相对于一直使用写锁,锁降级可以减少其他读线程的阻塞。
public void update() throws Exception {
// 获取写锁
reentrantReadWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread() + "【获取写锁】");
// 写数据到DB或者共享变量
// 获取读锁
reentrantReadWriteLock.readLock().lock();
} finally {
// 释放写锁
reentrantReadWriteLock.writeLock().unlock();
// 锁降级结束,降级为读锁
}
//当前线程消费数据,其他读线程(本博客没有实现)此时也可以消费数据
//当前线程消费数据完成,释放读锁
reentrantReadWriteLock.readLock().unlock();
}
2. 测试样例
完整测试代码:
import java.sql.Time;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 锁降级的学习
*/
public class LockDegradation implements Runnable {
/**
* 读写锁
*/
private ReentrantReadWriteLock reentrantReadWriteLock;
public LockDegradation() {
reentrantReadWriteLock = new ReentrantReadWriteLock();
}
@Override
public void run() {
while (true) {
try {
update();
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void update() throws Exception {
reentrantReadWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread() + "【获取写锁】");
System.out.println(Thread.currentThread() + "写数据到DB或者共享变量");
TimeUnit.SECONDS.sleep(1);
reentrantReadWriteLock.readLock().lock();
System.out.println(Thread.currentThread() + "【获取读锁】");
} finally {
reentrantReadWriteLock.writeLock().unlock();
System.out.println(Thread.currentThread() + "【释放写锁】锁降级结束,降级为读锁");
// 锁降级结束,降级为读锁
}
System.out.println(Thread.currentThread() + "当前线程消费数据,其他读线程(本博客没有实现)此时也可以消费数据");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread() + "当前线程消费数据完成,释放读锁");
reentrantReadWriteLock.readLock().unlock();
}
public static void main(String[] args) {
LockDegradation lockDegradation = new LockDegradation();
Thread thread1 = new Thread(lockDegradation);
Thread thread2 = new Thread(lockDegradation);
Thread thread3 = new Thread(lockDegradation);
thread1.start();
thread2.start();
thread3.start();
while (true) {
}
}
}
运行结果:
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=57496:/Applications/IntelliJ IDEA.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/lib/tools.jar:/Users/shiheng/testworkspace/JavaGrammarLearning/target/classes:/Users/shiheng/software/repository/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar:/Users/shiheng/software/repository/com/vdurmont/emoji-java/5.1.1/emoji-java-5.1.1.jar:/Users/shiheng/software/repository/org/json/json/20170516/json-20170516.jar:/Users/shiheng/software/repository/com/alibaba/fastjson/1.2.62/fastjson-1.2.62.jar:/Users/shiheng/software/repository/org/apache/poi/poi/4.1.1/poi-4.1.1.jar:/Users/shiheng/software/repository/commons-codec/commons-codec/1.13/commons-codec-1.13.jar:/Users/shiheng/software/repository/org/apache/commons/commons-collections4/4.4/commons-collections4-4.4.jar:/Users/shiheng/software/repository/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar:/Users/shiheng/software/repository/org/apache/poi/poi-ooxml/4.1.1/poi-ooxml-4.1.1.jar:/Users/shiheng/software/repository/org/apache/commons/commons-compress/1.19/commons-compress-1.19.jar:/Users/shiheng/software/repository/org/apache/xmlbeans/xmlbeans/3.1.0/xmlbeans-3.1.0.jar:/Users/shiheng/software/repository/com/alibaba/easyexcel/1.1.1/easyexcel-1.1.1.jar:/Users/shiheng/software/repository/commons-beanutils/commons-beanutils/1.9.2/commons-beanutils-1.9.2.jar:/Users/shiheng/software/repository/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar:/Users/shiheng/software/repository/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar:/Users/shiheng/software/repository/cglib/cglib/2.2/cglib-2.2.jar:/Users/shiheng/software/repository/asm/asm/3.1/asm-3.1.jar:/Users/shiheng/software/repository/io/reactivex/rxjava2/rxjava/2.2.21/rxjava-2.2.21.jar:/Users/shiheng/software/repository/org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar LockDegradation
Thread[Thread-0,5,main]【获取写锁】
Thread[Thread-0,5,main]写数据到DB或者共享变量
Thread[Thread-0,5,main]【获取读锁】
Thread[Thread-0,5,main]【释放写锁】锁降级结束,降级为读锁
Thread[Thread-0,5,main]当前线程消费数据,其他读线程(本博客没有实现)此时也可以消费数据
Thread[Thread-0,5,main]当前线程消费数据完成,释放读锁
Thread[Thread-1,5,main]【获取写锁】
Thread[Thread-1,5,main]写数据到DB或者共享变量
Thread[Thread-1,5,main]【获取读锁】
Thread[Thread-1,5,main]【释放写锁】锁降级结束,降级为读锁
Thread[Thread-1,5,main]当前线程消费数据,其他读线程(本博客没有实现)此时也可以消费数据
Thread[Thread-1,5,main]当前线程消费数据完成,释放读锁
Thread[Thread-2,5,main]【获取写锁】
Thread[Thread-2,5,main]写数据到DB或者共享变量
Thread[Thread-2,5,main]【获取读锁】
Thread[Thread-2,5,main]【释放写锁】锁降级结束,降级为读锁
Thread[Thread-2,5,main]当前线程消费数据,其他读线程(本博客没有实现)此时也可以消费数据
Thread[Thread-2,5,main]当前线程消费数据完成,释放读锁
Thread[Thread-0,5,main]【获取写锁】
Thread[Thread-0,5,main]写数据到DB或者共享变量
Thread[Thread-0,5,main]【获取读锁】
Thread[Thread-0,5,main]【释放写锁】锁降级结束,降级为读锁
Thread[Thread-0,5,main]当前线程消费数据,其他读线程(本博客没有实现)此时也可以消费数据
Thread[Thread-0,5,main]当前线程消费数据完成,释放读锁
Thread[Thread-1,5,main]【获取写锁】
Thread[Thread-1,5,main]写数据到DB或者共享变量
Thread[Thread-1,5,main]【获取读锁】
Thread[Thread-1,5,main]【释放写锁】锁降级结束,降级为读锁
Thread[Thread-1,5,main]当前线程消费数据,其他读线程(本博客没有实现)此时也可以消费数据
Thread[Thread-1,5,main]当前线程消费数据完成,释放读锁
Thread[Thread-2,5,main]【获取写锁】
Thread[Thread-2,5,main]写数据到DB或者共享变量
Thread[Thread-2,5,main]【获取读锁】
Thread[Thread-2,5,main]【释放写锁】锁降级结束,降级为读锁
Thread[Thread-2,5,main]当前线程消费数据,其他读线程(本博客没有实现)此时也可以消费数据
Thread[Thread-2,5,main]当前线程消费数据完成,释放读锁
...
参考文献:
- Java并发编程的艺术 / 方腾飞,魏鹏,魏晓明著 . ——北京:机械工业出版社,2015.7