zoukankan      html  css  js  c++  java
  • 简单线程同步互斥问题


    -如果限定只用一个线程读文件,其他三个线程处理文本,你要怎么做。

    -我想这个问题可以采用与读者写者问题类似的方法来解决。具体应该可以用以下几种方式实现:

    不上锁、不使用信号量的话,可以直接设置状态量,判断是否在读取、计算字符、计算行数等,例如开始时设isRead为true,读取完文件后,设为false;其他部分检测到为false,开始执行各自的计数操作。这与读取完文件后再发送通知其实类似。

    或者可以设置信号量,读者和写者间设置mutex1,互斥地进行读取、计数操作;读者间设置mutex2,互斥地访问读者计数量。

    Java中还有一个重入锁ReentrantLock,可以设置readLock和writeLock,在只有一个写操作时使读操作并行,也可以完成需要的功能。

    对于上面提出的三个方法:状态量、信号量和重入锁,我进行了简化实现。

    GitHub


    状态量

    设置一个状态量isRead,判断是否在进行读入操作,初始值为true。当读入操作结束后设为false,开始进行计数操作。这种方式限于只需第一次读入的情况。以下是部分主要代码:

        static class fileReader implements Runnable {
            public void run() {
                InputStreamReader inputStreamReader = null;
                BufferedReader bufferedReader = null;
                int in = 0;
    
                try {
                    inputStreamReader = new InputStreamReader
                            (new FileInputStream("input.txt"));
    
                    if (inputStreamReader != null) {
                        bufferedReader = new BufferedReader(inputStreamReader);
                    }
    
                    while ((in = bufferedReader.read()) != -1) {
                        file.append((char) in);
                    }
    
                    //设置标志,完成读入
                    isRead = false;
    
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        inputStreamReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        static class countChar implements Callable<Long> {
            public Long call() {
                int i = 0;
                while (1 > 0) {
                    if (!isRead) {
                        while (i < file.length()) {
                            charNum++;
                            i++;
                        }
                        break;
                    }
                }
                return charNum;
            }
        }
    

    信号量

    设置一个同步信号量和两个互斥信号量,同步信号用于确保第一次操作为读入操作,两个互斥信号用于完成“读者写者”,即读入部分与计数部分互斥,计数部分间不互斥。这种方法经过简单修改就可实现多次读入多次计数。以下是部分主要代码:

        static class fileReader implements Runnable {
            public void run() {
                InputStreamReader inputStreamReader = null;
                BufferedReader bufferedReader = null;
                int in = 0;
    
                try {
                    //进行读入时不允许计数
                    rmutex.acquire();
                    inputStreamReader = new InputStreamReader
                            (new FileInputStream("input.txt"));
    
                    if (inputStreamReader != null) {
                        bufferedReader = new BufferedReader(inputStreamReader);
                    }
    
                    while ((in = bufferedReader.read()) != -1) {
                        file.append((char) in);
                    }
    
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        inputStreamReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
    
                    rmutex.release();
                    //完成一次读入后才能进行计数
                    vmutex.release(2);
                }
            }
        }
    
        static class countChar implements Callable<Long> {
            public Long call() {
                int i = 0;
                while (1 > 0) {
                    try {
                        //允许计数
                        vmutex.acquire();
                        cmutex.acquire();
                        //进行计数时不允许读入
                        if (count==0) {
                            rmutex.acquire();
                        }
                        //添加“读者”
                        count++;
                        cmutex.release();
    
                        while (i < file.length()) {
                            charNum++;
                            i++;
                        }
    
                        cmutex.acquire();
                        //完成“阅读”
                        count--;
                        if (count==0) {
                            rmutex.release();
                        }
                        cmutex.release();
                        break;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                return charNum;
            }
        }
    

    重入锁

    设置一个读写锁和一个同步信号量,同步信号用于确保第一次操作为读入操作,读写锁用于完成“读者写者”,即读入部分与计数部分互斥,计数部分间不互斥。同样地,这种方法经过简单修改就可实现多次读入多次计数,而且还具有降级功能(写锁可降级为读锁)。以下是部分主要代码:

        static class fileReader implements Runnable {
            public void run() {
                InputStreamReader inputStreamReader = null;
                BufferedReader bufferedReader = null;
                int in = 0;
    
                try {
                    inputStreamReader = new InputStreamReader
                            (new FileInputStream("input.txt"));
    
                    if (inputStreamReader != null) {
                        bufferedReader = new BufferedReader(inputStreamReader);
                    }
    
                    //加上写锁,不允许其他进程写或读
                    lock.writeLock().lock();
                    while ((in = bufferedReader.read()) != -1) {
                        file.append((char) in);
                    }
                    //释放写锁
                    lock.writeLock().unlock();
                    //允许计数
                    vmutex.release(2);
    
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        inputStreamReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        static class countChar implements Callable<Long> {
            public Long call() {
                int i = 0;
    
                while (1>0) {
                    try {
                        //允许计数
                        vmutex.acquire();
                        //加上读锁,其他进程可读不可写
                        lock.readLock().lock();
                        while (i < file.length()) {
                            charNum++;
                            i++;
                        }
                        //释放读锁
                        lock.readLock().unlock();
                        break;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                return charNum;
            }
        }
    

    性能比较

    随机生成一千万次单词数据,经测试,三者时间相近,总时间都在1500ms左右,由此可知在基础的应用中三种方法基本没有性能差异。

  • 相关阅读:
    Bzoj4627 [BeiJing2016]回转寿司
    Bzoj1901 Zju2112 Dynamic Rankings
    COGS728. [网络流24题] 最小路径覆盖问题
    Bzoj4568 [Scoi2016]幸运数字
    Bzoj2728 [HNOI2012]与非
    HDU4609 3-idiots
    Bzoj2194 快速傅立叶之二
    Bzoj2179 FFT快速傅立叶
    模拟52 题解
    模拟51 题解
  • 原文地址:https://www.cnblogs.com/S031602240/p/9659004.html
Copyright © 2011-2022 走看看