zoukankan      html  css  js  c++  java
  • Java多线程设计模式(3)读写锁模式

    1 Read-Write Lock Pattern

    Read-Write Lock Pattern是一种将对于共享资源的访问与修改操作分离,称为读写分离。即访问是reader,修改是write,用单独的线程来处理。可以允许多个reader,但是不允许同时多个写入或者在读的过程中有写入

    由于对于实例状态的读取,并不会破坏状态的完整性且状态也不会修改,可以允许多个线程同时访问操作。但是若在写入的过程中,会更改实例的状态,此时就需要对于写入做保护,防止其他线程来进行读操作和写操作。

    在多个线程共享一个实例的时候,会有参考实例状态的线程即仅仅读取实例状态的Reader参与者,并且也会有改变实例的状态的线程即Writer参与者,此时就需要运用这种模式。

      这种模式在读取不会冲突下,允许多个Reader参与者同时reader,提高了性能。不用单纯的在每次读的时候利用Synchronized来使得只能允许一个线程读操作,而是利用了外部定义的逻辑锁来设置允许多个Reader同时操作。在读取的操作比写入的操作多的时候,也可以使用这个模式。

      在这个模式中,主要有四个参与者,分别为:

    1 Reader参与者。Reader参与者会对SharedResource参与者进行read。

    2 Writer参与者。Writer参与者会对SharedResource参与者进行write。

    3 SharedResource参与者。这个是Reader参与者与Writer参与者共享的资源。里面包含了提供不会改变内部状态的操作read,与会改变内部状态的操作write。注意对于这个参与者,不需要在进行read与write的方法加上synchronized。

    在对于SharedResource参与者进行read和write操作时候,利用Before/After Pattern实现。

        前置处理(获取锁定)

          try{

          实际操作

           }finally{

            后续处理(释放锁)}

    利用finally可以保证无论发生什么情况,都会释放锁,也不能把前置处理放置在try中,如果放置在里面,则finally就一定会执行,当前置处理发生异常,就不应该进而执行finally

      4 ReadWriteLock参与者。提供了对于SharedResource参与者进行read和write时需要的锁,就是用户自定义的锁机制。包括readLock和readUnLock锁,writeLock和writeUnLock,注意必须要对这些操作进行同步处理,放置多个线程同时调用这些方法,在每个方法中必须加上synchronized。

    实例:

    有多个读者和多个写者共同操作一本书,只允许同时多个读者读取书,只允许同时一个写者写这本书,不允许同时读者与写者同时操作这本书籍。

    SharedResource参与者。

    在每次读写操作之间和之后都要获取锁与释放锁,不需要synchronized

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    package whut.readwritelock;
    public class Data {
        private final char[] buffer;
        private final ReadWriteLock lock=new ReadWriteLock();
                                                                                                                                                                                                                                                                                                        
        public Data(int size)
        {
            this.buffer=new char[size];
            for(int i=0;i<buffer.length;i++)
                buffer[i]='*';
        }
                                                                                                                                                                                                                                                                                                        
        public char[] read()throws InterruptedException
        {
            lock.readLock();
            try{
                return doRead();
            }finally{
                lock.readUnlock();
            }
        }
                                                                                                                                                                                                                                                                                                        
        private char[] doRead()
        {
            char[] newbuf=new char[buffer.length];
            for(int i=0;i<buffer.length;i++)
                newbuf[i]=buffer[i];
            slowly();
            return newbuf;
        }
                                                                                                                                                                                                                                                                                                        
        public void write(char c)throws InterruptedException
        {
            lock.writeLock();
            try{
                 doWrite(c);
            }finally{
                lock.writeUnlock();
            }
        }
                                                                                                                                                                                                                                                                                                        
        private void doWrite(char c)
        {
            for(int i=0;i<buffer.length;i++)
            {
                buffer[i]=c;
                slowly();
                //这里的sleep并不会切换到别的线程
                //这里就是体现了使用while的好处
                //当该线程sleep时候,其余等待读取的还在wait中,而要写入的线程会判断它的状态,还没有释放锁
            }
        }
                                                                                                                                                                                                                                                                                                        
        private void slowly()
        {
            try{
                Thread.sleep(50);
            }catch(InterruptedException e)
            {
                                                                                                                                                                                                                                                                                                                
            }
        }
                                                                                                                                                                                                                                                                                                        
    }


    读者线程Reader

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package whut.readwritelock;
    public class ReaderThread extends Thread{
        private final Data data;
        public ReaderThread(Data data)
        {
            this.data=data;
        }
                                                                                                                                                                                                                                                                                               
        public void run()
        {
            try{
                while(true)
                {
                    char[] readbuf=data.read();
                    System.out.println(Thread.currentThread().getName()
                            +" reads "+String.valueOf(readbuf));
                }
            }catch(InterruptedException e)
            {
            }
        }
    }

    写者线程Writer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    package whut.readwritelock;
    import java.util.Random;
    public class WriterThread extends Thread{
                                                                                                                                                                                                                                                                                       
        private static final Random random=new Random();
        private final Data data;
        private final String filler;
        private int index=0;
                                                                                                                                                                                                                                                                                       
        public WriterThread(Data data,String filler)
        {
            this.data=data;
            this.filler=filler;
        }
                                                                                                                                                                                                                                                                                       
        public void run()
        {
            try{
                while(true)
                {
                    char c=nextChar();
                    data.write(c);
                    Thread.sleep(random.nextInt(1000));
                }
            }catch(InterruptedException e)
            {
                                                                                                                                                                                                                                                                                               
            }
        }
                                                                                                                                                                                                                                                                                       
        private char nextChar()
        {
            char c=filler.charAt(index);
            index++;
            if(index>=filler.length())
                index=0;
            return c;
        }
    }

    WriteReadLock类

     主要就是利用自定义锁机制来控制读写操作。设置读写时的警戒条件,利用Guarded Suspension Pattern来处理。

      设置有四个字段:

    readingReaders表示实例当前被读取的线程数目。是readLock之后与readUnlock之间的数。作为写入操作的警戒条件之一。

      writingReaders表示实例当被写操作的线程数目,只能是0或者1,它是作为写入操作和读取操作的警戒条件。

    waitingReaders表示多少个处于等待写入的线程数目。当ReaderThread比WriterThread多的时候,由于在要进行写入操作过程中,如果有读取操作,则会一直等待,但是读取操作没有设置为互斥,则他们会一个个的执行读操作,致使写入操作无法进行。设置这个字段状态目的就是为了使得ReaderThread在警戒条件判断中当waitingReaders大于0的时候,该ReaderThread能wait,能让出控制权给WriterThread。

    preferWriter表示true为写优先,表示false为读优先。虽然读取能让出控制权给写入操作,可是当写入操作拥有了控制权后,则可能使得读取操作无法执行。此时就需要这个preferWriter字段,这个就是目的使得ReaderThread和WriterThread能够轮流执行的。

       当读操作完后,设置为true以使得写入操作有可能获得执行,不过写入操作必须在满足preferWriter为true以及有等待写入的操作下才能真正的写入。当写入操作完毕后,设置该字段为false使得读操作能够执行。

    总结:

      读取操作等待的警戒条件是:当前写入线程大于0,或者preferWriter为true且有等待写入的线程

      写入操作等待的警戒条件是:当前写入线程大于0,或者当前读取线程大于0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    package whut.readwritelock;
    //关键部分
    public class ReadWriteLock {
        private int readingReaders=0;//实际正在读取的线程数目
        private int waitingWriters=0;//正在等待写入的线程数目
        private int writingWriters=0;//实际正在写入的线程数目
        private boolean preferWriter=true;//写入优先的话,值为true
                 
        //读取的时候获取锁
        public synchronized void readLock()throws InterruptedException
        {
            //当有写入的时候,或者写入为优先级并且有等待的写入线程
            while(writingWriters>0||(preferWriter&&waitingWriters>0))
            {
                wait();
            }
            readingReaders++;
        }
                 
        //读完毕后释放锁
        public synchronized void readUnlock()throws InterruptedException
        {
            readingReaders--;
            preferWriter=true;
            notifyAll();
        }
                 
        //写入的时候获取锁
        public synchronized void writeLock()throws InterruptedException
        {
            waitingWriters++;//正在等待的写入的线程数目
            try{
                //有写入或者读入的时候
                while(readingReaders>0||writingWriters>0)
                {
                    wait();
                }
            }finally{
                waitingWriters--;//被唤醒了,则就是进而真正写入
            }
            writingWriters++;
        }
                 
        //写入毕后释放锁
        public synchronized void writeUnlock()throws InterruptedException
        {
            writingWriters--;
            preferWriter=false;//写入后马上更换优先级,让读者继续
            notifyAll();
        }
    }

    测试类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package whut.readwritelock;
    public class ReadWriteMain {
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Data data=new Data(10);
            //读取线程
            new ReaderThread(data).start();
            new ReaderThread(data).start();
            new ReaderThread(data).start();
            new ReaderThread(data).start();
            new ReaderThread(data).start();
            new ReaderThread(data).start();
               
            //写入线程
            new WriterThread(data,"ABCDEFGHIJKLMNOPQRSTUVWXYZ").start();
            new WriterThread(data,"abcdefghijklmnopqrstuvwxyz").start();
        }
    }



    本文出自 “在云端的追梦” 博客,请务必保留此出处http://computerdragon.blog.51cto.com/6235984/1202752

  • 相关阅读:
    JavaWeb--HttpSession案例
    codeforces B. Balls Game 解题报告
    hdu 1711 Number Sequence 解题报告
    codeforces B. Online Meeting 解题报告
    ZOJ 3706 Break Standard Weight 解题报告
    codeforces C. Magic Formulas 解题报告
    codeforces B. Sereja and Mirroring 解题报告
    zoj 1109 Language of FatMouse 解题报告
    hdu 1361.Parencodings 解题报告
    hdu 1004 Let the Balloon Rise 解题报告
  • 原文地址:https://www.cnblogs.com/daichangya/p/12959276.html
Copyright © 2011-2022 走看看