读写锁

为什么读写要互斥,读的时候不能写,写的时候不能读?
举个例子,用户A正在读的时候,用户B对数据进行了写操作,就会出现A读到的数据不准确。A在写的时候,B去读,则读到的数据也是不准确的。所以读和写要互斥。

读写锁的特点

1、读锁与写锁互斥。
2、读锁之间不互斥。
3、写锁与写锁互斥。

读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这些特性由jvm底层控制,代码中加上相应的锁即可。使用的时候注意,读的时候上读锁,写的时候上写锁就OK!

读写锁对应的api

ReadWriteLock(接口)/ReentrantReadWriteLock(实现类)
ReentrantReadWriteLock有两个静态的内部类
ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock
他们的类声明如下
public static class WriteLock implements Lock, java.io.Serializable
public static class ReadLock implements Lock, java.io.Serializable

package cn.iktz.thread.demo;

import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockTest2 {
    public static void main(String args[])  {
        final ThreadData threadData = new ThreadData();
        //for循环会开启6个线程,3个线程get数据,3个线程put数据,
        //当然,get到的数据,都是最后一个put进入的数据。
        for (int i = 0; i < 3; i++) {
            new Thread() {
                public void run() {
                    while (true) {
                        try {
                            threadData.get();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }

            }.start();

            new Thread() {
                public void run(){
                    while (true) {
                        try {
                            threadData.put(new Random().nextInt(10000));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }

            }.start();
        }
    }

    static class ThreadData {
        private Object data = null;
        ReadWriteLock rwl = new ReentrantReadWriteLock();

        public void get() throws Exception {
            rwl.readLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + " --- get method start >>");
                Thread.sleep((long) (Math.random() * 1000));
                System.out.println(Thread.currentThread().getName() + " --- get method end >> data :" + data);
            } finally {
                rwl.readLock().unlock();
            }
        }

        public void put(Object data) throws Exception {
            rwl.writeLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + " ||| put method start >>");
                Thread.sleep((long) (Math.random() * 1000));
                this.data = data;
                System.out.println(Thread.currentThread().getName() + " ||| put method end >> data :" + data);
            } finally {
                rwl.writeLock().unlock();
            }
        }
    }
}

java api中关于读写锁实现CacheData的例子

package cn.iktz.thread.demo;

import java.util.concurrent.locks.ReentrantReadWriteLock;

class LoginUser{
    public int id;
    public String loginName;
    public String password;
    public static LoginUser getLoginUser(int id){
        LoginUser loginUser = new LoginUser();
        //dao.selectLoginUserByID(id)
        return loginUser ;
    }
}
class CachedData {
    LoginUser loginUser;
    volatile boolean cacheValid;
    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    void processCachedData() {
        rwl.readLock().lock();
        if (!cacheValid) {//第一次读数据的时候,LoginUser是空的
            // Must release read lock before acquiring write lock
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            // Recheck state because another thread might have acquired
            // write lock and changed state before we did.
            if (!cacheValid) {//如果没有缓存,
                loginUser = LoginUser.getLoginUser(01);
                cacheValid = true;
            }
            // Downgrade by acquiring read lock before releasing write lock
            rwl.readLock().lock();
            rwl.writeLock().unlock(); // Unlock write, still hold read
        }

        //应用数据
        System.out.println(loginUser);
        rwl.readLock().unlock();
    }
}

缓存的逻辑

这里写图片描述

用hashMap实现的一个简易的缓存系统

package cn.iktz.thread.demo;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class CacheDemo {
    private Map<String, Object> cache = new HashMap<String, Object>();
    private ReadWriteLock rwl = new ReentrantReadWriteLock();

    public Object getData(String key) {
        rwl.readLock().lock();
        Object obj = null;
        try {
            obj = cache.get(key);
            if (obj == null) {
                rwl.readLock().unlock();
                rwl.writeLock().lock();
                try {
                    if (obj == null) {
                        // 查询数据库,
                    }
                } finally {
                    rwl.writeLock().unlock();
                }
                rwl.readLock().lock();
            }
        } finally {
            rwl.readLock().unlock();
        }
        return obj;
    }
}

为何要进行第二次的验证null?

因为,若不判断空,当有n个线程(n>2)走到 rwl.readLock().unlock();rwl.writeLock().lock();的时候,当上了写锁以后,其他的线程就阻塞在了这里,在这里等待,当第一个线程获取了数据往下走了以后,以前等待在这里的线程继续往下走,可能会又去查一次数据。
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值