为什么会存在读写锁呢?
1、因为synchronized粒度太大了,并不适合我们,可重入锁的粒度相较于读锁(共享锁)也较大,我们需要粒度小的锁。
2、大部分场景下,读不需要加锁,而写需要加锁,因为写入不加锁有可能出现写入覆盖和信息不一致的情况,并且大部分的读需求粒度更小的锁,这样会占用更少的资源。
独占锁(写锁)—次只能被一个线程占有
共享锁(读锁)多个线程可以同时占有
这里先不加锁
import java.util.HashMap;
import java.util.Map;
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.set(num+"",num+"");
}).start();
}
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.get(num+"");
}).start();
}
}
}
class MyCache{
private volatile Map<String,String> map=new HashMap();
public void set(String key,String value){
System.out.println("准备存入键中:"+key);
map.put(key,value);
System.out.println(key+"存入了");
}
public void get(String key){
System.out.println("获取到了键"+key);
String value = map.get(key);
System.out.println("取到了键"+key+"里的值:"+value);
}
}
会发现set过程中会被其他线程插队:
准备存入键中:1
准备存入键中:2
2存入了
1存入了
准备存入键中:3
3存入了
准备存入键中:5
5存入了
准备存入键中:4
获取到了键2
取到了键2里的值:2
获取到了键4
取到了键4里的值:null
获取到了键1
取到了键1里的值:1
4存入了
获取到了键3
获取到了键5
取到了键5里的值:5
取到了键3里的值:3
如果我们在写入的时候不想要其他线程插队,那么就要引入读写锁:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.set(num+"",num+"");
}).start();
}
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.get(num+"");
}).start();
}
}
}
class MyCache{
private volatile Map<String,String> map=new HashMap();
private ReadWriteLock lock=new ReentrantReadWriteLock();
public void set(String key,String value){
lock.writeLock().lock();
try {
System.out.println("准备存入键中:"+key);
map.put(key,value);
System.out.println(key+"存入了");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
public void get(String key){
lock.readLock().lock();
try {
System.out.println("获取到了键"+key);
String value = map.get(key);
System.out.println("取到了键"+key+"里的值:"+value);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}
结果:
准备存入键中:1
1存入了
准备存入键中:2
2存入了
准备存入键中:3
3存入了
准备存入键中:4
4存入了
准备存入键中:5
5存入了
获取到了键1
取到了键1里的值:1
获取到了键2
取到了键2里的值:2
获取到了键5
获取到了键4
取到了键4里的值:4
获取到了键3
取到了键3里的值:3
取到了键5里的值:5
Process finished with exit code 0
写锁由于是独占锁,所以在输出完毕前并不会被抢占。但读锁是共享锁,所以并不会被锁影响,还是会继续输出