在原先介绍的ReentrantLock的使用中,多个线程使用锁都是排他的,即上锁的代码同一时间只有一个线程可以执行。这在有些场景下是不合理,低效的。
比如我们需要有两个线程同时读某个变量的值进行一些处理,反正也没有线程会更改变量的值,不可能造成线程不安全的脏读,为什么要排他地运行呢?只有线程会更改变量的时候才需要排他。所以针对这种情况,读写锁ReentrantReadWriteLock才有了用武之地。
一、读读锁不互斥
@Slf4j
public class Counter {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void write() {
lock.readLock().lock();
try {
log.info("当前线程{}获取了读锁!", Thread.currentThread().getName());
// 故意等待5秒不释放锁
Thread.sleep(5000);
} catch (Exception e) {
log.info("{}", e);
} finally {
lock.readLock().unlock();
log.info("当前线程{}释放了读锁!", Thread.currentThread().getName());
}
}
}
@Slf4j
public class MyThread implements Runnable {
private Counter counter;
public MyThread(Counter counter){
this.counter = counter;
}
@Override
public void run() {
counter.write();
}
}
@Slf4j
public class Test001 {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread thread1 = new Thread(new MyThread(counter));
thread1.start();
Thread thread2 = new Thread(new MyThread(counter));
thread2.start();
}
}
执行结果可以看出,两个线程几乎同时都获取了读锁,其中任一线程的独占时间并不影响其它线程获取读锁。
当前线程Thread-1获取了读锁!
当前线程Thread-0获取了读锁!
当前线程Thread-0释放了读锁!
当前线程Thread-1释放了读锁!
二、写写锁互斥
将上例中的读锁全部换成写锁:
public void write() {
lock.writeLock().lock();
try {
log.info("当前线程{}获取了写锁!", Thread.currentThread().getName());
// 故意等待5秒不释放锁
Thread.sleep(5000);
} catch (Exception e) {
log.info("{}", e);
} finally {
lock.writeLock().unlock();
log.info("当前线程{}释放了写锁!", Thread.currentThread().getName());
}
}
就会发现,线程之间互斥执行:
当前线程Thread-0获取了写锁!
当前线程Thread-0释放了写锁!
当前线程Thread-1获取了写锁!
当前线程Thread-1释放了写锁!
三、其它
其它诸如读写、写读的情况,只要多线程中有获取写锁的线程,那么它与其它线程之间都是互斥的。
网友评论