简单线程同步互斥问题


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

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

不上锁、不使用信号量的话,可以直接设置状态量,判断是否在读取、计算字符、计算行数等,例如开始时设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左右,由此可知在基础的应用中三种方法基本没有性能差异。

原文地址:https://www.cnblogs.com/S031602240/p/9659004.html