为什么我们有了Lock,还要用ReadWriteLock呢。我们对共享资源加锁之后,所有的线程都将会等待。Lock读操作也锁,写操作也会锁,而对共享资源读的时候,其实是不用加锁的。当然读写同时存在的情况也会有。
比如我们数据库常用操作有增删改查,增删改都是写操作,写操作必须加锁,而读操作可以共享。不是所有的操作都需要加锁。
为了进一步提高复用性和粒度,写操作独占,读操作共享,不加锁。
ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。
即读读可共享,写读写写要独占
读操作不用关心资源争抢和数据一致性的操作
多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行。
但是如果有一个线程想去写共享资源来,就不应该再有其它线程可以对该资源进行读或写。
小总结:
读-读能共存
读-写不能共存
写-写不能共存
ReadWriteLock适用于读多写少的并发情况。
Java并发包中ReadWriteLock是一个接口,主要有两个方法,如下:
public interface ReadWriteLock { /** * 返回读锁 */ Lock readLock(); /** * 返回写锁 */ Lock writeLock(); }
1
2
3
4
5
6
7
8
9
10
11
Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性。
2.不用读写锁的案例class MyCache{ //volalite保证数据再线程间的可见性和一定的有序性 private volatile Map<String,Object> map=new HashMap<>(); public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"\t ---写入数据"+key); map.put(key,value); try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();} System.out.println(Thread.currentThread().getName()+"\t ---写入完成"); } public void get(String key){ System.out.println(Thread.currentThread().getName()+"\t 读取数据"); Object result=map.get(key); System.out.println(Thread.currentThread().getName()+"\t 读取完成"+result); } } public class ReadWriteLockDemo { public static void main(String[] args) { // TODO Auto-generated method stub MyCache myCache=new MyCache(); for(int i=1;i<=5;i++){ final int tempInt=i; new Thread(()->{ myCache.put(tempInt+"",tempInt+""); },String.valueOf(i)).start(); } for(int i=1;i<=5;i++){ final int tempInt=i; new Thread(()->{ myCache.get(tempInt+""); },String.valueOf(i)).start(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
上面的运行结果出现了一个很严重的问题,比如说,线程1在写入数据的时候,还没等线程1写完,其它线程也在写和读。这违背了事务的原子性。而且也破坏了数据的完整性和一致性,我正在写的时候,不允许其他线程同时也在写或者读。即1写入的时候,必须要满足后一条就是1写入完成。
class MyCache{ //volalite保证数据再线程间的可见性和一定的有序性 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()+"\t ---写入数据"+key); map.put(key,value); try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();} System.out.println(Thread.currentThread().getName()+"\t ---写入完成"); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); }finally{ readwritelock.writeLock().unlock(); } } public void get(String key){ readwritelock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"\t 读取数据"); Object result=map.get(key); System.out.println(Thread.currentThread().getName()+"\t 读取完成"+result); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); }finally{ readwritelock.readLock().unlock(); } } } public class ReadWriteLockDemo { public static void main(String[] args) { // TODO Auto-generated method stub MyCache myCache=new MyCache(); for(int i=1;i<=5;i++){ final int tempInt=i; new Thread(()->{ myCache.put(tempInt+"",tempInt+""); },String.valueOf(i)).start(); } for(int i=1;i<=5;i++){ final int tempInt=i; new Thread(()->{ myCache.get(tempInt+""); },String.valueOf(i)).start(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55