• Java高并发,ReadWriteLock(读写锁)


    并发读写的时候,很容易造成数据不一致的状态

    上案例,代码如下:

    public class ReadWriteLockDemo {
        public static void main(String[] args) {
            MyCache myCache = new MyCache();
            for (int i = 0; i < 5; i++) {
                final int finali= i;
                new Thread(() -> {
                    try {
                        myCache.put(finali+"", finali+"");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }, String.valueOf(i)).start();
            }
    
            for (int i = 0; i < 5; i++) {
                final int finali= i;
                new Thread(() -> {
                    try {
                        myCache.get(finali+"");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }, String.valueOf(i)).start();
            }
        }
    }
    
    class MyCache {
        private volatile Map<String, Object> map = new HashMap<>();
    
        public void put(String key, Object value) throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + "\t-----写入数据key");
            Thread.sleep(3000);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t-----写入数据成功");
        }
    
        public void get(String key) throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + "\t读取数据key");
            Thread.sleep(3000);
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t读取数据成功" + result);
        }
    }

    运行结果如下:

    我们可以看到的是在1进行写入数据的时候,此时还没有写入成功,就已经对1进行了读取操作,就像我们数据库的原子性一样,这里在还没有对数据进行写入完成就进行了读取的操作,所以读取的为空。
    接下来我们看看加入了读写锁的效果,这里只需要对MyCache进行修改:

    加入ReadWriteLock

    ReadWriteLock的作用:保证并发读

    • 写锁是独占锁,所谓独占即为独自占有,别的线程既不能获取到该锁的写锁,也不能获取到对应的读锁。
    • 读锁是共享锁,所谓共享即是所有线程都可以共同持有该读锁
    • 归纳与一句换:读读共存 读写不共存 写写不共存

    代码如下 :

    class MyCache {
        private volatile Map<String, Object> map = new HashMap<>();
        private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        public void put(String key, Object value) throws InterruptedException {
            readWriteLock.writeLock().lock();  //上写锁
            try {
                System.out.println(Thread.currentThread().getName() + "\t-----写入数据key");
                Thread.sleep(3000);
                map.put(key, value);
                System.out.println(Thread.currentThread().getName() + "\t-----写入数据成功");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readWriteLock.writeLock().unlock();
            }
        }
        public void get(String key) throws InterruptedException {
            readWriteLock.readLock().lock();  //上读锁
            try {
                System.out.println(Thread.currentThread().getName() + "\t读取数据key");
                Thread.sleep(3000);
                Object result = map.get(key);
                System.out.println(Thread.currentThread().getName() + "\t读取数据成功" + result);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readWriteLock.readLock().unlock();
            }
        }
    }

    运行结果如下:

     可以看到我们对写保持了一致性,读保证了可并发读,防止了在写的时候进行了读的操作导致的不一致性,所以这就是读写锁的作用 

    ReadWriteLock锁降级

    锁降级过程(当前线程):

    为什么可以降级? 为什么在写锁释放之前可以拿到读锁?

    首先写锁是独占的,读锁是共享的,然后读写锁是线程间互斥的,锁降级的前提是所有线程都希望对数据变化敏感,但是因为写锁只有一个,所以会发生降级。

    你既然拿到写锁了,其他线程就没法拿到读锁或者写锁了,你再拿读锁,其实不会和其他线程的写锁发送冲突的,因为你拿到写锁到写锁释放这段时间其他线程是无法拿到任何锁的。

    注意以下情况不是锁降级

    如果先释放写锁,再获取读锁,可能在获取之前,会有其他线程获取到写锁,阻塞读锁的获取,就无法感知数据变化了。所以需要先hold住写锁,保证数据无变化,获取读锁,然后再释放写锁。

    其他线程在该线程释放写锁之前,写操作所做的数据更新对其他线程是不可见的。但是一旦写锁释放,数据更新操作就会对其他线程可见。

    思考?

    即使是先释放写锁,然后获取读锁可能也没有问题,只不过会可能会被其他线程的写锁阻塞一段时间;

    但是并不意味着,随后的这个读操作看不到之前别的线程的写锁下的写操作,只要写锁被释放数据更新还是可以看到的,所以说,上述这句话“阻塞读锁的获取,那么当前线程无法感知线程T的数据更新” 感觉有些瑕疵

    但是需要明确的一点:用锁降级的前提是读优先于写。

    比如常见的查询系统,需要保证数据的随时可读,如果当新线程请求读锁的时候,当前持有写锁的线程需要马上进行降级,保证所有读锁的顺利获取,阻塞后续写锁。

  • 相关阅读:
    【转】vue常用开发ui框架(app,后台管理系统,移动端)及插件
    【转】VUE 组件转换为微信小程序组件的方法
    【转】微信小程序原生代码快速转换mpuve(vue)代码(仅供娱乐)
    goim Go 开发的 IM 和推送服务
    h5 canvas 多张图片合成并保存到手机相册
    netcore中设置环境变量ASPNETCORE_ENVIRONMENT
    Cookie 和 Session 区别是什么?
    python中的字符串格式化输出
    模型训练时样本类别不均衡怎么办?
    NumPy 数组索引、维度增加、拼接
  • 原文地址:https://www.cnblogs.com/codehaogg/p/13544875.html
Copyright © 2020-2023  润新知