• Java线程锁之意难平的读写锁


    之前在做一个项目的时候,遇到了这样一个问题:

    读操作可以同时进行,写操作不能同时进行,读写操作不能同时进行。

    通俗来说,就是当执行读操作的时候,除了不能写入之外,其他都行。但是,当执行写操作的时候,除了当前操作之外,什么都做不了。

    当时一怒之下暴力加锁,管你什么操作,全都给我一个一个执行,这样的话,当然是可以,但是也极大的削弱了项目的性能。

    后来接触到了读写锁这个东西,粗略看了一下貌似正是应对这种场景的,但是,源码暂时有点不大想研究,于是决定用自己的想法实现一个简易版的读写锁。

    在此过程中发现自己实际上对于一些线程方面的知识还不够熟悉,有的概念在运用的时候才察觉到并没有领会透彻,先记录一下这次学习的过程。

    import lombok.SneakyThrows;
    
    import java.util.Set;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    public class ReadWriteLock {
    
        private static Set<Long> state = new CopyOnWriteArraySet<>();
        private static volatile int reading = 0;
        private static volatile int writing = 0;
        private static volatile int readWait = 0;
        private static volatile int writeWait = 0;
        private static Object READ_LOCK = new Object();
        private static Object WRITE_LOCK = new Object();
    
        public static void read() throws InterruptedException {
            for (;;) {
                synchronized (READ_LOCK) {
                    if (writing != 0) {
                        if (!state.contains(getThreadId())) {
                            readWait++;
                            state.add(getThreadId());
                        }
                        READ_LOCK.wait();
                    } else {
                        reading++;
                        if (state.contains(getThreadId())) {
                            readWait--;
                            state.remove(getThreadId());
                        }
                        break;
                    }
                }
            }
        }
    
        public static void readComplete() {
                synchronized (READ_LOCK) {
                    reading--;
                }
            if (writeWait != 0) {
                synchronized (WRITE_LOCK) {
                    WRITE_LOCK.notify();
                }
            }
        }
    
        public static void write() throws InterruptedException {
            for (;;) {
                synchronized (WRITE_LOCK) {
                    if (writing != 0 || reading != 0) {
                        if (!state.contains(getThreadId())) {
                            writeWait++;
                            state.add(getThreadId());
                        }
                        WRITE_LOCK.wait();
                    } else {
                        writing++;
                        if (state.contains(getThreadId())) {
                            writeWait--;
                            state.remove(getThreadId());
                        }
                        break;
                    }
                }
            }
        }
    
        public static void writeComplete() {
            synchronized (WRITE_LOCK) {
                writing--;
            }
    
            if (writeWait >= readWait) {
                synchronized (WRITE_LOCK) {
                    WRITE_LOCK.notify();
                }
            } else {
                synchronized (READ_LOCK) {
                    READ_LOCK.notifyAll();
                }
            }
        }
    
        public static Long getThreadId() {
            return Thread.currentThread().getId();
        }
    
        public static void main(String[] args) throws InterruptedException {
            for (int j = 0; j < 500; j++) {
                final int[] read = {0};
                final int[] write = {0};
                Object readLock = new Object();
                Object writeLock = new Object();
    
                for (int i = 0; i < 5000; i++) {
                    new Thread(new Runnable() {
                        @SneakyThrows
                        @Override
                        public void run() {
                            ReadWriteLock.read();
                            //System.out.println("read:" + Thread.currentThread().getName());
                            synchronized (readLock) {
                                read[0]++;
                            }
                            ReadWriteLock.readComplete();
                        }
                    }, String.valueOf(i)).start();
                }
    
                for (int i = 0; i < 5000; i++) {
                    new Thread(new Runnable() {
                        @SneakyThrows
                        @Override
                        public void run() {
                            ReadWriteLock.write();
                            //System.out.println("write:" + Thread.currentThread().getName());
                            synchronized (writeLock) {
                                write[0]++;
                            }
                            ReadWriteLock.writeComplete();
                        }
                    }, String.valueOf(i)).start();
                }
    
                Thread.sleep(2000);
                if (read[0] == write[0]) {
                    read[0] = 0;
                    write[0] = 0;
                } else {
                    throw new RuntimeException("Thread not safe");
                }
            }
        }
    }

    主方法中进行了500次测试,最终没有抛出异常,那么我暂时认为这个简易版的读写锁是可用的。

    state容器用于存储线程id,线程只有在第一次进入等待池前,才会将id存入state,后续用于判断该线程是新创建的线程还是处于等待状态的线程,从而决定是否将等待数量减一。

    写的时候感觉有点小饶,可能是因为没有在写之前把所有可能发生的线程安全等问题罗列出来,导致在写的过程中发现问题,修改代码,写到这里忽然想到另一个需要注意的问题,如果代码中可能报错,那么最好将释放锁的语句写在finally里,否则加锁后得不到释放,可能会死锁。

  • 相关阅读:
    IT运维监控解决方案介绍
    Apdex——衡量服务器性能的标准
    服务器监控(包括性能指标与web应用程序)
    使用厂商MIB库查找设备OID值并实施监控的方法
    华为USG6550 MIB CPU MEM
    LInux下实时网络流量监控工具nload教程
    11gR2 集群(CRS/GRID)新功能—— SCAN(Single Client Access Name)
    如何实现网卡bond
    LeetCode(31)-Factorial Trailing Zeroes
    activiti自己定义流程之整合(五):启动流程时获取自己定义表单
  • 原文地址:https://www.cnblogs.com/wxdmw/p/14010668.html
Copyright © 2020-2023  润新知