9.读写锁

回顾悲观锁和乐观锁的概念
悲观锁: 见字知意,他是干什么都很悲观,所以在操作的时候,每次都上锁,使用时解锁
乐观锁:他很乐观,多线程,并不上锁,但是会发生 线程安全问题


表锁:整个表操作,不会发生死锁
行锁:每个表中的单独一行进行加锁,会发生死锁
读锁:共享锁(可以有多个人读),会发生死锁
写锁:独占锁(只能有一个人写),会发生死锁

关于读写锁


读写锁:一个资源可以被多个读线程访问,也可以被一个写线程访问,但不能同时存在读写线程,读写互斥,读读共享

读写锁ReentrantReadWriteLock
读锁为ReentrantReadWriteLock.ReadLock,readLock()方法
写锁为ReentrantReadWriteLock.WriteLock,writeLock()方法

创建读写锁对象private ReadWriteLock rwLock = new ReentrantReadWriteLock();
写锁 加锁 rwLock.writeLock().lock();,解锁为rwLock.writeLock().unlock();
读锁 加锁rwLock.readLock().lock();,解锁为rwLock.readLock().unlock();
 

 

案例分析:
模拟多线程在map中取数据和读数据

//资源类
class MyCache {
    //创建map集合
    private volatile Map<String,Object> map = new HashMap<>();
 
    //创建读写锁对象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();
 
    //放数据
    public void put(String key,Object value) {
        //添加写锁
        rwLock.writeLock().lock();
 
        try {
            System.out.println(Thread.currentThread().getName()+" 正在写操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 写完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            rwLock.writeLock().unlock();
        }
    }
 
    //取数据
    public Object get(String key) {
        //添加读锁
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName()+" 正在读取操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 取完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            rwLock.readLock().unlock();
        }
        return result;
    }
}
 
public class ReadWriteLockDemo {
    public static void main(String[] args) throws InterruptedException {
        MyCache myCache = new MyCache();
        //创建线程放数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.put(num+"",num+"");
            },String.valueOf(i)).start();
        }
        TimeUnit.MICROSECONDS.sleep(300);
        //创建线程取数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

总结锁的演变

    无锁:多线程抢夺资源
    synchronized和ReentrantLock,都是独占,每次只可以一个操作,不能共享
    ReentrantReadWriteLock,读读可以共享,提升性能,但是不能多人写。缺点:造成死锁(一直读,不能写),读进程不能写,写进程可以读。
    写锁降级为读锁(一般等级写锁高于读锁)

具体第四步演练的代码
具体降级步骤
获取写锁->获取读锁->释放写锁->释放读锁

//演示读写锁降级
public class Demo1 {
    public static void main(String[] args) {
        //可重入读写锁对象
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();//读锁
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//写锁
        //锁降级
        //1 获取写锁
        writeLock.lock();
        System.out.println("manongyanjiuseng");
        //2 获取读锁
        readLock.lock();
        System.out.println("---read");
        //3 释放写锁
        writeLock.unlock();
        //4 释放读锁
        readLock.unlock();
    }
}

如果是读之后再写,执行不了    因为读锁权限小于写锁 需要读完之后释放读锁,在进行写锁

//2 获取读锁
readLock.lock();
System.out.println("---read");
//1 获取写锁
writeLock.lock();
System.out.println("manongyanjiuseng");

 

posted @ 2022-03-12 19:42  随遇而安==  阅读(150)  评论(0编辑  收藏  举报