多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行。 但是,如果有一个线程想去共享资源类,就不应该再有其他线程可以对该资源进行读或写。
Java并发包中ReadWriteLock是一个接口,主要有两个方法,如下
实现类ReentrantLock在并发情况下只允许单个线程执行受保护的代码,而在大部分应用中都是读多写少,所以,如果使用ReentrantLock实现这种对共享数据的并发访问控制,将严重影响整体的性能。
-
读取锁(ReadLock)可以实现并发访问下的多读
-
写入锁(WriteLock)可以实现每次只允许一个写操作。
但是,在它的实现中,读、写是互斥的,即允许多读,但是不允许读写和写读同时发生。
class MyCache{
private volatile Map<String,Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key,Object value){
//写锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入中…………");
//模拟写入时间300毫秒
TimeUnit.MICROSECONDS.sleep(300);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入成功!!");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//打开写锁
readWriteLock.writeLock().unlock();
}
}
public void get(String key){
//读锁
//加了读锁就是共享锁,所有人都可以读,但是写会阻塞,因为在读时可能正在写,此时会报并发修改异常的
//读锁是共享锁,写锁是排它锁
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取中…………");
//模拟读取时间300毫秒
TimeUnit.MICROSECONDS.sleep(300);
map.get(key);
System.out.println(Thread.currentThread().getName()+"读取成功!!");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//打开写锁
readWriteLock.readLock().unlock();
}
}
}
public class _读写锁 {
public static void main(String[] args) {
MyCache myCache = new MyCache();
//写入5个线程
for (int i = 1; i <= 5; i++) {
int finalI = i;
new Thread(()->{
myCache.put(finalI+"",finalI+"");
},String.valueOf(i)).start();
}
//读取5个线程
for (int i = 1; i <= 5; i++) {
int finalI = i;
new Thread(()->{
myCache.get(finalI+"");
},String.valueOf(i)).start();
}
}
}
使得对写保持了一致性,读保证了可并发读,防止了在写的时候进行了读的操作,所以这就是读写锁的作用。
- 小总结
多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行。
但是,如果有一个线程想去写共享资源来,就不应该再有其他线程可以对改资源进行读或写:
- 读-读能共存
- 读-写不能共存
- 写-写不能共存