zoukankan      html  css  js  c++  java
  • 可重入的读写锁

      读写锁同样存在着重入问题。简单的读写锁见读写锁浅析。这里我们拿这个简单的写锁来做一个重入测试:

        @Test
        public void testNonReentrantLock()
        {
            MyReadWriteLock lock = new MyReadWriteLock();
            
            new Thread(() -> {
                {
                    try
                    {
                        lock.writeLock();
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    System.out.println("我是写锁.");
                    
                    // 我又来拿读锁了
                    try
                    {
                        lock.writeLock();
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    System.out.println("我还是写锁.");
                    
                    lock.writeUnlock();
                    System.out.println("解放写锁.");
                    
                    lock.writeUnlock();
                    System.out.println("解放写锁.");
                    
                }
            }).start();
        }

      输出结果:

    我是写锁.

      后续的写锁没法加了,说明并不支持重入。要支持重入,就先得认得之前加的锁,要认得之前加的锁也简单,先认得加锁所在的线程就行了。看实现:

    package com.wulinfeng.test.testpilling.util;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 可重入的读写锁
     *
     * @author wulinfeng
     * @version C10 2019年1月14日
     * @since SDP V300R003C10
     */
    public class MyReentrantReadWriteLock
    {
        // 读线程映射
        private Map<Thread, Integer> readThreads = new HashMap<>();
        
        // 写线程
        private Thread writeThread = null;
        
        // 写线程
        private int write = 0;
        
        // 写请求线程
        private int writeRequest = 0;
        
        public synchronized void readLock()
            throws InterruptedException
        {
            // 当前读线程
            Thread readThread = Thread.currentThread();
            
            // 加读锁
            while (!accessRead(readThread))
            {
                wait();
            }
            
            // 取到锁,重入次数自增
            readThreads.put(readThread, getReadCount(readThread) + 1);
        }
        
        public synchronized void readUnlock()
        {
            // 当前线程
            Thread currentThread = Thread.currentThread();
            
            // 是否为读线程,不是则报错
            if (!isRead(currentThread))
            {
                throw new IllegalMonitorStateException("Calling Thread does not hold a read lock on this ReadWriteLock");
            }
            
            // 获取读线程的重入次数
            int readCount = getReadCount(currentThread);
            
            // 解放读锁
            if (readCount == 1)
            {
                readThreads.remove(currentThread);
            }
            else
            {
                // 还存在重入次数则自减
                readThreads.put(currentThread, readCount - 1);
            }
            
            notifyAll();
        }
        
        public synchronized void writeLock()
            throws InterruptedException
        {
            // 写请求累加
            writeRequest++;
            
            // 获取当前写进程
            Thread currentThread = Thread.currentThread();
            
            // 加写锁
            while (!accessWrite(writeThread))
            {
                wait();
            }
            
            // 写请求自减
            writeRequest--;
            
            // 写线程自增
            write++;
            
            writeThread = currentThread;
        }
        
        public synchronized void writeUnlock()
        {
            if (!isWrite(Thread.currentThread()))
            {
                throw new IllegalMonitorStateException("Calling Thread does not hold a write lock on this ReadWriteLock");
            }
            
            // 写线程自减
            write--;
            
            // 解放写线程
            if (write == 0)
            {
                writeThread = null;
            }
            
            notifyAll();
        }
        
        /**
         * 是否允许加读锁
         *
         * @author wulinfeng
         * @param readThread
         * @return
         */
        private boolean accessRead(Thread currentThread)
        {
            // 当前线程为写线程,则允许读
            if (isWrite(currentThread))
            {
                return true;
            }
            
            // 存在写线程则不允许读
            if (hasWrite())
            {
                return false;
            }
            
            // 当前为读线程则允许读
            if (isRead(currentThread))
            {
                return true;
            }
            
            // 存在写请求则不允许读
            if (hasWriteRequest())
            {
                return false;
            }
            return true;
        }
        
        /**
         * 是否允许加写锁
         *
         * @author wulinfeng
         * @param writeThread
         * @return
         */
        private boolean accessWrite(Thread writeThread)
        {
            // 只有一个读锁,允许写
            if (isOnlyRead(writeThread))
            {
                return true;
            }
            
            // 存在读锁,不允许写
            if (hasRead())
            {
                return false;
            }
            
            // 写线程为空,允许写
            if (writeThread == null)
            {
                return true;
            }
            
            // 当前线程不为写线程(那就是读线程了),不允许写
            if (!isWrite(writeThread))
            {
                return false;
            }
            return true;
        }
        
        /**
         * 获取读线程的重入次数
         *
         * @author wulinfeng
         * @param readThread
         * @return
         */
        private int getReadCount(Thread readThread)
        {
            Integer readCount = readThreads.get(readThread);
            
            if (readCount == null)
            {
                return 0;
            }
            return readCount.intValue();
        }
        
        /**
         * 读线程不为空
         *
         * @author wulinfeng
         * @return
         */
        private boolean hasRead()
        {
            return readThreads.size() > 0;
        }
        
        /**
         * 当前线程是否读线程
         *
         * @author wulinfeng
         * @param currentThread
         * @return
         */
        private boolean isRead(Thread currentThread)
        {
            return readThreads.get(currentThread) != null;
        }
        
        /**
         * 是否只有一个读线程
         *
         * @author wulinfeng
         * @param readThread
         * @return
         */
        private boolean isOnlyRead(Thread readThread)
        {
            return readThreads.size() == 1 && readThreads.get(readThread) != null;
        }
        
        /**
         * 是否存在写线程
         *
         * @author wulinfeng
         * @return
         */
        private boolean hasWrite()
        {
            return writeThread != null;
        }
        
        /**
         * 当前线程是否为写线程
         *
         * @author wulinfeng
         * @param currentThread
         * @return
         */
        private boolean isWrite(Thread currentThread)
        {
            return writeThread == currentThread;
        }
        
        /**
         * 是否存在写请求
         *
         * @author wulinfeng
         * @return
         */
        private boolean hasWriteRequest()
        {
            return this.writeRequest > 0;
        }
    }

      我们把之前的测试代码中MyReadWriteLock改为MyReentrantReadWriteLock,这次输出结果就对了:

    我是写锁.
    我还是写锁.
    解放写锁.
    解放写锁.

      最后我们测试一下实际的应用:

        @Test
        public void TestMyReentrantReadWriteLock()
        {
            MyReentrantReadWriteLock lock = new MyReentrantReadWriteLock();
            
            new Thread(() -> {
                {
                    BufferedReader br = null;
                    
                    // 加锁
                    try
                    {
                        lock.readLock();
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    
                    // 读文件
                    try
                    {
                        br = new BufferedReader(new InputStreamReader(new FileInputStream(FILE_PATH), "GBK"));
                        int lineNo = 0;
                        String lineContent = null;
                        while ((lineContent = br.readLine()) != null)
                        {
                            System.out.printf("行号:%d:%s
    ", lineNo, lineContent);
                            lineNo++;
                        }
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                        try
                        {
                            br.close();
                        }
                        catch (IOException e)
                        {
                            e.printStackTrace();
                        }
                    }
                    
                    // 解锁
                    lock.readUnlock();
                }
            }).start();
            
            latch.countDown();
            
            // 起个线程写,写的内容可以多一点
            new Thread(() -> {
                {
                    String content = "人们对杭州的了解,更多地源自曾风靡一时的电视剧《新白娘子传奇》,还有鲁迅笔下那倒掉的雷峰塔。";
                    BufferedWriter bw = null;
                    
                    // 加锁
                    try
                    {
                        lock.writeLock();
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    
                    // 写入
                    try
                    {
                        System.out.println("开始写....");
                        bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(FILE_PATH, true), "GBK"));
                        bw.write(content);
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                        try
                        {
                            bw.close();
                        }
                        catch (IOException e3)
                        {
                            e3.printStackTrace();
                        }
                    }
                    
                    // 解锁
                    lock.writeUnlock();
                }
            }).start();
            
            latch.countDown();
            
            new Thread(() -> {
                {
                    BufferedReader br = null;
                    
                    // 加锁
                    try
                    {
                        lock.readLock();
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    
                    // 读文件
                    try
                    {
                        br = new BufferedReader(new InputStreamReader(new FileInputStream(FILE_PATH), "GBK"));
                        int lineNo = 0;
                        String lineContent = null;
                        while ((lineContent = br.readLine()) != null)
                        {
                            System.out.printf("行号:%d:%s
    ", lineNo, lineContent);
                            lineNo++;
                        }
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                        try
                        {
                            br.close();
                        }
                        catch (IOException e)
                        {
                            e.printStackTrace();
                        }
                    }
                    
                    // 解锁
                    lock.readUnlock();
                }
            }).start();
            
            latch.countDown();
            
            // 先休息一会儿
            try
            {
                Thread.sleep(1000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            
            // 主线程等待
            try
            {
                latch.await();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            
        }

      输出结果:

    文件不存在。
    行号:0:细雨之中的西湖、盛开的荷花、一座断桥,淡淡几笔,足以勾勒出淡妆浓抹总相宜的杭州。
    行号:1:杭州之美,在于留给旁人对美的无尽想象空间。对我们大多数人来说,杭州的美,犹如一幅盛满故事的山水画,诗意、神秘、动情;对于航天之父钱学森来说,杭州于他也是这般美丽。
    行号:2:只是直到19岁,他才能借养病之机认识自己家乡的美丽,到底有点迟了。
    行号:3:但钱学森一触摸到杭州这幅美卷,便充满不舍和一生的惦念。
    行号:4:虽然钱学森年少时因父亲工作调动而辗转生活于北京、上海,长大后为了学业穿梭于北京和大洋彼岸的美国,但杭州,终究是钱学森生命开始的地方,这里听到过他的第一声啼哭,雕刻过他的第一个脚印——他是踏莲而生的,他先着地的双脚,让杭州望族钱家越发枝叶繁茂。
    开始写....
    行号:0:细雨之中的西湖、盛开的荷花、一座断桥,淡淡几笔,足以勾勒出淡妆浓抹总相宜的杭州。
    行号:1:杭州之美,在于留给旁人对美的无尽想象空间。对我们大多数人来说,杭州的美,犹如一幅盛满故事的山水画,诗意、神秘、动情;对于航天之父钱学森来说,杭州于他也是这般美丽。
    行号:2:只是直到19岁,他才能借养病之机认识自己家乡的美丽,到底有点迟了。
    行号:3:但钱学森一触摸到杭州这幅美卷,便充满不舍和一生的惦念。
    行号:4:虽然钱学森年少时因父亲工作调动而辗转生活于北京、上海,长大后为了学业穿梭于北京和大洋彼岸的美国,但杭州,终究是钱学森生命开始的地方,这里听到过他的第一声啼哭,雕刻过他的第一个脚印——他是踏莲而生的,他先着地的双脚,让杭州望族钱家越发枝叶繁茂。
    行号:5:人们对杭州的了解,更多地源自曾风靡一时的电视剧《新白娘子传奇》,还有鲁迅笔下那倒掉的雷峰塔。
  • 相关阅读:
    微软一站式技术框架(AllInOne Code Framework(AIO)) 登陆 MSDN中国首页
    The stepbystep guide of making a C# (or VB.NET) Windows 7 Trigger Start Service
    AllInOne Code Framework: 微软一站式开发技术框架 示例命名规则和目录结构说明
    Windows 7 新特性 Shell Library 编程接口介绍
    一步一步教你如何编写VC#,VB.NET或VC++代码玩转Windows Shell Known Folders
    微软一站式开发技术框架解决方案 年内展望
    AllInOne Code Framework: 微软一站式开发技术框架解决方案 2009730 新增sample code简介
    AllInOne Code Framework: 微软一站式开发技术框架 2009917 新增代码示例简介
    AllInOne Code Framework: 微软一站式开发技术框架解决方案 2009826 新增sample code简介
    微软 AllInOne Code Framework (AIO) 客户端应用程序 期待您的见解
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/10266814.html
Copyright © 2011-2022 走看看