特征: 写写互斥、读写互斥、读读共享
锁降级: 写线程获取写入锁或可以获取读取锁, 然后释放写入锁, 这样就从写入锁变成了读取锁, 从而实现锁降级的特征
先看一个错误例子
public class ReentrantReadWriteLockDemo {
private int i = 0;
private int j = 0;
public void out() {
System.out.println(Thread.currentThread().getName() + "的值i=" + i + "\tj=" + j);
}
public void inCreate() {
i++;
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
e.printStackTrace();
}
j++;
}
public static void main(String[] args) {
ReentrantReadWriteLockDemo reentrantReadWriteLockDemo = new ReentrantReadWriteLockDemo();
for (int i = 0; i < 3; i++) {
new Thread(() -> {
reentrantReadWriteLockDemo.inCreate();
reentrantReadWriteLockDemo.out();
}).start();
}
}
}
结果分析:j的值还没读取到就返回了, 所以不能得到预期的结果
那么上面的例子该怎么改呢?
- 两个方法都加上synchronize关键字修饰,这时候无论在读或写都要获取锁,但是这是一种很浪费资源的写法, 有性能开销问题(不推荐)
- 利用ReentrantReadWriteLock
public class ReentrantReadWriteLockDemo {
private int i = 0;
private int j = 0;
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private Lock readLock = reentrantReadWriteLock.readLock();
private Lock writeLock = reentrantReadWriteLock.writeLock();
public void out() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "的值i=" + i + "\tj=" + j);
} catch (Exception e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
}
public void inCreate() {
writeLock.lock();
try {
i++;
Thread.sleep(200L);
j++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
public static void main(String[] args) {
ReentrantReadWriteLockDemo reentrantReadWriteLockDemo = new ReentrantReadWriteLockDemo();
for (int i = 0; i < 3; i++) {
new Thread(() -> {
reentrantReadWriteLockDemo.inCreate();
reentrantReadWriteLockDemo.out();
}).start();
}
}
}
结果
写写互斥、读写互斥、读读共享可以通过多线程debug可以看出来
这里不做演示了, 提一下多线程debug操作注意的地方
快捷键ctrl + shit + f8 更改如下
在这里可以查看各个线程的运行情况