在JDK中提供了一种读写锁ReentrantReadWriteLock类,相比ReentrantLock类,使用前者可以加快运行效率。ReentrantLock类是具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务,这样做虽然保证了实例变量的线程安全性,但效率却是非常低下的。所以在某些不需要操作实例变量的方法中,完全可以使用读写锁ReentrantReadWriteLock来提升该方法的代码运行速度。
目录
- 读写锁简介
- 一、读读共享
- 二、写写互斥
- 三、读写互斥
- 四、写读互斥
读写锁简介
读写锁表示也有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。在没有线程Thread进行写入操作时,进行读取操作的多个Thread都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进行写入操作。即多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。
下面我们来看ReentrantReadWriteLock类的使用实例:
一、读读共享
创建一个读写服务类ReadWriteService.java,代码如下:
public class ReadWriteService {
// 创建读写锁
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 读方法
public void read() {
try {
try {
lock.readLock().lock();
System.out.println("获取读锁" + Thread.currentThread().getName()
+ "==" + System.currentTimeMillis());
Thread.sleep(5000);
} finally {
lock.readLock().unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
创建两个线程,调用读写服务。代码如下:
线程一
public class Thread1 extends Thread {
private ReadWriteService readWriteService;
public Thread1(ReadWriteService readWriteService){
super();
this.readWriteService = readWriteService;
}
@Override
public void run() {
readWriteService.read();
}
}
线程二
public class Thread2 extends Thread{
private ReadWriteService readWriteService;
public Thread2(ReadWriteService readWriteService){
super();
this.readWriteService = readWriteService;
}
@Override
public void run() {
readWriteService.read();
}
}
启动线程,代码如下:
public class Main {
public static void main(String[] args) {
ReadWriteService service = new ReadWriteService();
Thread1 a = new Thread1(service);
a.setName("A");
Thread2 b = new Thread2(service);
b.setName("B");
a.start();
b.start();
}
}
输出如下:
从控制台中打印的时间来看,两个线程几乎同时进入lock()方法后面的代码。说明在此使用了lock.readLock()读锁的确可以提高程序运行效率,他允许多个线程同时执行lock()方法后面的代码。
二、写写互斥
改一下读写服务类ReadWriteService.java,代码如下:
public class ReadWriteService {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("获取写锁" + Thread.currentThread().getName()
+ "==" + System.currentTimeMillis());
Thread.sleep(5000);
} finally {
lock.writeLock().unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后修改一下两个线程中调用ReadWriteService的方法,启动
从运行结果可以看到使用写锁代码lock.writeLock()的效果就是同一时间只允许一个线程执行lock()方法后面的代码。
三、读写互斥
修改ReadWriteService.java代码
public class ReadWriteService {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
try {
lock.readLock().lock();
System.out.println("获取读锁" + Thread.currentThread().getName()
+ "==" + System.currentTimeMillis());
Thread.sleep(5000);
} finally {
lock.readLock().unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println("获取写锁" + Thread.currentThread().getName()
+ "==" + System.currentTimeMillis());
Thread.sleep(5000);
} finally {
lock.writeLock().unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
两个线程类中线程一调用读方法,线程二调用写方法;
然后修改启动类:
public class Main {
public static void main(String[] args) {
ReadWriteService service = new ReadWriteService();
Thread1 a = new Thread1(service);
a.setName("A");
a.start();
Thread2 b = new Thread2(service);
b.setName("B");
b.start();
}
}
输出:
由此可以说明“读写”操作也是互斥的,而且下一个示例说明了“写读”操作也是互斥的。也就是说,只要出现“写操作”的过程,就会是互斥的。
四、写读互斥
ReadWriteService.java代码还是跟读写互的一样,只要修改启动类线程执行顺序;代码如下:
public class Main {
public static void main(String[] args) {
ReadWriteService service = new ReadWriteService();
Thread2 b = new Thread2(service);
b.setName("B");
b.start();
Thread1 a = new Thread1(service);
a.setName("A");
a.start();
}
}
运行结果:
可以看到“写读”操作也是互斥的。
总结:
ReentrantReadWriteLock类是读写锁,他对于“读写”、“写读”和“写写”都是互斥的;而“读读”是异步的,非互斥的。也就是只要包含写操作,就是互斥的;但是对于读读就是共享的。即多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。