Java——多线程高并发系列之ReadWriteLock读写锁
Posted 张起灵-小哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java——多线程高并发系列之ReadWriteLock读写锁相关的知识,希望对你有一定的参考价值。
文章目录:
写在前面
synchronized 内部锁与 ReentrantLock 锁都是独占锁(排它锁),同一时间只允许一个线程执行同步代码块,可以保证线程的安全性,但是执行效率低。
ReentrantReadWriteLock 读写锁是一种改进的排他锁,也可以称作共享/排他锁。允许多个线程同时读取共享数据,但是一次只允许一个线程对共享数据进行更新。
读写锁通过读锁与写锁来完成读写操作,线程在读取共享数据前必须先持有读锁,该读锁可以同时被多个线程持有,即它是共享的。线程在修改共享数据前必须先持有写锁,写锁是排他的,一个线程持有写锁时其他线程无法获得相应的锁。
读锁只是在读线程之间共享,任何一个线程持有读锁时,其他线程都无法获得写锁,保证线程在读取数据期间没有其他线程对数据进行更新,使得读线程能够读到数据的最新值,保证在读数据期间共享变量不被修改。
获得条件 | 排他性 | 作用 | |
读锁 | 写锁未被任意线程持有 | 对读线程是共享的,对写线程是排他的 | 允许多个读线程可以同时读取共享数据,保证在读共享数据时,没有其他线程对共享数据进行修改 |
写锁 | 该写锁未被其他线程持有,并且相应的读锁也未被其他线程持有 | 对读线程或者写线程都是排他的 | 保证写线程以独占的方式修改共享数据 |
读写锁允许读读共享,读写互斥,写写互斥。
在java.util.concurrent.locks包中定义了ReadWriteLock接口,该接口中定义了 readLock()返回读锁,定义 writeLock()方法返回写锁,该接口的实现类是 ReentrantReadWriteLock。
注意readLock()与writeLock()方法返回的锁对象是同一个锁的两个不同的角色,不是分别获得两个不同的锁。ReadWriteLock 接口实例可以充当两个角色。
Demo1(读读共享)
ReadWriteLock 读写锁可以实现多个线程同时读取共享数据,即读读共享,可以提高程序的读取数据的效率。
package com.szh.lock.readwrite;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* ReadWriteLock 读写锁的读读共享,允许多个线程同时获得读锁
*/
public class Test01 {
static class Service {
//定义读写锁
ReadWriteLock rwLock=new ReentrantReadWriteLock();
//定义方法读取数据
public void read() {
try {
rwLock.readLock().lock(); //获得读锁
System.out.println(Thread.currentThread().getName() + "获得读锁,开始读取数据的时间:"
+ System.currentTimeMillis());
TimeUnit.MILLISECONDS.sleep(1000 * 3); //模拟读取数据用时
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock(); //释放读锁
}
}
}
public static void main(String[] args) {
Service s=new Service();
//创建 5 个线程,调用 read() 方法
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
s.read(); //在线程中调用 read()读取数据
}
}).start();
}
//运行程序后, 这几个线程几乎可以同时获得锁读, 执行 lock() 后面的代码
}
}
可以看到这五个子线程都可以顺利的获得读锁来读取数据。 即读读共享。
Demo2(写写互斥)
package com.szh.lock.readwrite;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 演示 ReadWriteLock 读写锁的写写互斥
* ReadWriteLock 的 writeLock() 写锁是互斥的, 只允许有一个线程持有
*/
public class Test02 {
static class Service {
//定义读写锁
ReadWriteLock rwLock=new ReentrantReadWriteLock();
//定义方法修改数据
public void write() {
try {
rwLock.writeLock().lock(); //申请获得写锁
System.out.println(Thread.currentThread().getName() + " 获得写锁,开始修改数据的时间:"
+ System.currentTimeMillis());
TimeUnit.MILLISECONDS.sleep(1000 * 3); //模拟修改数据的用时
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " 修改数据完毕时的时间== "
+ System.currentTimeMillis());
rwLock.writeLock().unlock(); //释放写锁
}
}
}
public static void main(String[] args) {
Service s=new Service();
//创建3个线程修改数据
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
s.write();
}
}).start();
}
//从执行结果来看, 同一时间只有一个线程获得写锁
}
}
可以看到,当Thread-0获得了写锁之后,在它修改完数据,释放写锁的这段时间内,其他线程是无法进来获得写锁的。其他线程要想获得写锁,必须等待其他线程修改完数据释放了写锁才可以。即写写互斥。
Demo3(读写互斥)
package com.szh.lock.readwrite;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 演示 ReadWriteLock 读写锁的读写互斥
* 一个线程获得读锁时,写线程等待; 一个线程获得写锁时,其他线程等待
*/
public class Test03 {
static class Service {
//定义读写锁
ReadWriteLock rwLock=new ReentrantReadWriteLock();
//定义方法读取数据
public void read() {
try {
rwLock.readLock().lock(); //申请获得读锁
System.out.println(Thread.currentThread().getName() + "获得读锁,开始读取数据的时间:"
+ System.currentTimeMillis());
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " 释放了读锁...");
rwLock.readLock().unlock(); //释放读锁
}
}
//定义方法修改数据
public void write() {
try {
rwLock.writeLock().lock(); //申请获得写锁
System.out.println(Thread.currentThread().getName() + " 获得写锁,开始修改数据的时间:"
+ System.currentTimeMillis());
TimeUnit.MILLISECONDS.sleep(1000 * 3); //模拟修改数据的用时
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " 修改数据完毕时的时间== "
+ System.currentTimeMillis());
rwLock.writeLock().unlock(); //释放写锁
}
}
}
public static void main(String[] args) {
Service s=new Service();
//定义一个线程读数据
new Thread(new Runnable() {
@Override
public void run() {
s.read();
}
}).start();
//定义一个线程写数据
new Thread(new Runnable() {
@Override
public void run() {
s.write();
}
}).start();
}
}
可以看到,当Thread-0获得了读锁之后,在它读取完数据之前,其他线程是无法进来获得写锁的(但是可以获得读锁),也就是保证了当前获得读锁的线程读取到的是最新的数据(未被其他线程修改的数据)。而当Thread-0读取完数据释放了读锁之后,Thread-1这个时候才可以获得写锁进行数据的修改,而当一个线程获得写锁的时候,其他任何线程都无法获得读锁和写锁。
以上是关于Java——多线程高并发系列之ReadWriteLock读写锁的主要内容,如果未能解决你的问题,请参考以下文章
Java——多线程高并发系列之线程池(Executor)的理解与使用
Java——多线程高并发系列之线程池(Executor)的理解与使用