多线程-读写锁

懵懂的女人 提交于 2020-03-06 09:00:35

1.读写锁

 

ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁。 Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性

假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需要一个读/写锁来解决这个问题。

对于lock的读写锁,可以通过new ReentrantReadWriteLock()获取到一个读写锁。所谓读写锁,便是多线程之间读不互斥,读写互斥。读写锁是一种自旋锁,如果当前没有读者,也没有写者,那么写者可以立刻获得锁,否则它必须自旋在那里,直到没有任何写者或读者。如果当前没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该锁

简单来说就是

独占锁(写锁):一次只能被一个线程占有 共享锁(读锁):该锁可以被多个线程占有!

先看一下没有锁的情况

package demo.ReadWriteLock;
​
import java.util.HashMap;
import java.util.Map;
​
public class ReadWriteLock {
    public static void main(String[] args) {
        Mycache mycache = new Mycache();
        for (int i = 1; i <=5 ; i++) {
            final int num = i;
            new Thread(()->{
                mycache.put(String.valueOf(num),num);
            },String.valueOf(i)).start();
        }
        for (int i = 1; i <=5 ; i++) {
            final int num = i;
            new Thread(()->{
                mycache.get(String.valueOf(num));
            },String.valueOf(i)).start();
        }
​
    }
}
​
class Mycache{
​
    private volatile Map<String,Object> map = new HashMap<>();
​
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"读取");
​
        Object o = map.get(key);
​
        System.out.println(Thread.currentThread().getName()+"读取结果"+o);
    }
​
​
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"写入");
​
        map.put(key,value);
​
        System.out.println(Thread.currentThread().getName()+"写入完毕");
​
    }
​
}

输出:

 

加锁

package demo.ReadWriteLock;
​
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
​
public class ReadWriteLocks {
    public static void main(String[] args) {
        MyCacheLock mycache = new MyCacheLock();
        for (int i = 1; i <=5 ; i++) {
            final int num = i;
            new Thread(()->{
                mycache.put(String.valueOf(num),num);
            },String.valueOf(i)).start();
        }
        for (int i = 1; i <=5 ; i++) {
            final int num = i;
            new Thread(()->{
                mycache.get(String.valueOf(num));
            },String.valueOf(i)).start();
        }
​
    }
​
}
​
// 加锁操作: 读写锁
class MyCacheLock{
​
    private volatile Map<String,Object> map = new HashMap<>();
    // 读写锁
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
​
    // 读 : 可以被多个线程同时读
    public void get(String key){
        // 这些锁一定要匹配,否则就可能导致死锁!
        readWriteLock.readLock().lock(); // 多个线程同时持有
        try {
            System.out.println(Thread.currentThread().getName()+"读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取结果:"+o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
​
    // 写 :应该是保证原子性 , 不应该被打扰
    public void put(String key,Object value){
​
        readWriteLock.writeLock().lock(); // 只能被一个线程占用
        try {
            System.out.println(Thread.currentThread().getName()+"写入" + key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入ok" );
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
​
}

输出:

 

总结

1.Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性

2.ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的

3.ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!