Java多线程设计模式(三)

 

Read-Wirte Lock Pattern

        Read-Write Lock Pattern 将读取和写入分开来处理。在读取数据之前,必须获取用来读取的锁定。而要写入的时候,则必须获取用来写入的锁定。因为进行读取时,实例的状态不会改变,所 以,就算有多个线程在同时读取也没有关系。但当有线程在进行写入的时候,不可以再进行写入的操作。写入的时候,实例的状态会改变。于是,当有一个线程在写 入的时候,其他线程就不可以进行读取或写入。一般来说,进行共享互斥会使程序性能变差,但将写入的共享互斥与读取的共享互斥拆分开来,就可以提高程序的性 能

 

[java] view plain copy
 
  1. public class ReadWriteLock {  
  2.   
  3.     /** 
  4.      * 正在读取的线程数 
  5.      */  
  6.     private int     readingThreadsNumber;  
  7.   
  8.     /** 
  9.      * 正在写入的线程数(最多为1) 
  10.      */  
  11.     private int     writingThreadsNumber;  
  12.   
  13.     /** 
  14.      * 等待写入的线程数 
  15.      */  
  16.     private int     waitingWriteThreadsNumber;  
  17.   
  18.     /** 
  19.      * 是否优先写入,true:优先写入;false:优先读取 
  20.      */  
  21.     private boolean preferWriter = true;  
  22.   
  23.     public synchronized void readLock() throws InterruptedException {  
  24.         // 如果有线程正在写入或者优先写入时,有线程正在等待写入,读取线程则等待  
  25.         while (this.writingThreadsNumber > 0  
  26.                 || (this.preferWriter && this.waitingWriteThreadsNumber > 0)) {  
  27.             wait();  
  28.         }  
  29.   
  30.         this.readingThreadsNumber++;  
  31.     }  
  32.   
  33.     public synchronized void readUnlock() throws InterruptedException {  
  34.         this.readingThreadsNumber--;  
  35.         this.preferWriter = true;  
  36.         notifyAll();  
  37.     }  
  38.   
  39.     public synchronized void writeLock() throws InterruptedException {  
  40.         this.waitingWriteThreadsNumber++;  
  41.         // 如果有线程正在写入或者正在读取,当前写入线程等待  
  42.         try {  
  43.             while (this.writingThreadsNumber > 0 || this.readingThreadsNumber > 0) {  
  44.                 wait();  
  45.             }  
  46.         } finally {  
  47.             this.waitingWriteThreadsNumber--;  
  48.         }  
  49.   
  50.         this.writingThreadsNumber++;  
  51.     }  
  52.   
  53.     public synchronized void writeUnlock() throws InterruptedException {  
  54.         this.writingThreadsNumber--;  
  55.         this.preferWriter = false;  
  56.         notifyAll();  
  57.     }  
  58.   
  59. }  

[java] view plain copy
 
  1. public class Data {  
  2.   
  3.     private char[]        buffer;  
  4.   
  5.     private ReadWriteLock readWriteLock = new ReadWriteLock();  
  6.   
  7.     public Data(int size) {  
  8.         this.buffer = new char[size];  
  9.         for (int i = 0; i < size; i++) {  
  10.             this.buffer[i] = '*';  
  11.         }  
  12.     }  
  13.   
  14.     public char[] read() throws InterruptedException {  
  15.         try {  
  16.             readWriteLock.readLock();  
  17.             return doRead();  
  18.         } finally {  
  19.             readWriteLock.readUnlock();  
  20.         }  
  21.     }  
  22.   
  23.     public void write(char c) throws InterruptedException {  
  24.         try {  
  25.             readWriteLock.writeLock();  
  26.             doWrite(c);  
  27.         } finally {  
  28.             readWriteLock.writeUnlock();  
  29.         }  
  30.     }  
  31.   
  32.     private char[] doRead() {  
  33.         char[] newChars = new char[buffer.length];  
  34.         System.arraycopy(this.buffer, 0, newChars, 0this.buffer.length);  
  35.         slowly();  
  36.         return newChars;  
  37.     }  
  38.   
  39.     private void doWrite(char c) {  
  40.         for (int i = 0; i < this.buffer.length; i++) {  
  41.             this.buffer[i] = c;  
  42.             slowly();  
  43.         }  
  44.     }  
  45.   
  46.     private void slowly() {  
  47.         try {  
  48.             Thread.sleep(100);  
  49.         } catch (InterruptedException e) {  
  50.         }  
  51.     }  
  52. }  

 


[java] view plain copy
 
  1. import java.util.Random;  
  2.   
  3. public class ReaderThread extends Thread {  
  4.   
  5.     private static final Random random = new Random();  
  6.   
  7.     private final Data          data;  
  8.   
  9.     public ReaderThread(Data data) {  
  10.         this.data = data;  
  11.     }  
  12.   
  13.     @Override  
  14.     public void run() {  
  15.         while (true) {  
  16.             try {  
  17.                 char[] c = data.read();  
  18.                 System.out.println(Thread.currentThread().getName() + "reads " + String.valueOf(c));  
  19.                 Thread.sleep(random.nextInt(1000));  
  20.             } catch (InterruptedException e) {  
  21.             }  
  22.         }  
  23.     }  
  24.   
  25. }  


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


[java] view plain copy
 
  1. public class MainThread {  
  2.   
  3.     public static void main(String[] args) {  
  4.         int bufferSize = 10;  
  5.         Data data = new Data(bufferSize);  
  6.   
  7.         new ReaderThread(data).start();  
  8.         new ReaderThread(data).start();  
  9.         new ReaderThread(data).start();  
  10.         new ReaderThread(data).start();  
  11.         new ReaderThread(data).start();  
  12.         new ReaderThread(data).start();  
  13.         new ReaderThread(data).start();  
  14.   
  15.         String filler1 = "abcdefghjklmnopqrstuvwxyz";  
  16.         String filler2 = "ABCDEFGHJKLMNOPQRSTUVWXYZ";  
  17.         new WriterThread(data, filler1).start();  
  18.         new WriterThread(data, filler2).start();  
  19.   
  20.     }  
  21.   
  22. }  
        单纯使用Single Thread Execution Pattern时,就连read的操作一次也只有一条线程可以执行。如果read操作比较频繁或比较耗时,那使用Read-Write Lock Pattern会比Single Thread Execution Pattern好很多。但因为Read-Write Lock Pattern的程序比Single Thread Execution Pattern实现复杂,如果read操作很简单,那么使用Single Thread Execution Pattern可能性能反而较高。 Read-Write Lock Pattern的优点在于Reader参与者之间不会起冲突。不过,当wirte操作比较频繁时,Writer参与者会经常阻挡Reader参与者的进 行,这样就无法展现Read-Write Lock Pattern的优点。

        在ReadWriteLock类中,提供了“读访问锁定”和“写访问锁定”两种逻辑上的锁定,但在“物理”上只用到了一个锁定,即ReadWriteLock实例的锁定。




Thread-Per-Message Pattern

thread per message 就是每一个消息一个线程。对每一个命令或请求,分配一个线程,由这个线程执行工作,这就是Thread-Per-Message Pattern。

 

[java] view plain copy
 
  1. public class Helper {  
  2.   
  3.     public void handle(int count, char c) {  
  4.         System.out.println("handle(" + count + ", " + c + ") BEGIN");  
  5.         for (int i = 0; i < count; i++) {  
  6.             System.out.print(c);  
  7.             slowly();  
  8.         }  
  9.         System.out.println("");  
  10.         System.out.println("handle( " + count + ", " + c + ") END");  
  11.     }  
  12.   
  13.     private void slowly() {  
  14.         try {  
  15.             Thread.sleep(50);  
  16.         } catch (InterruptedException e) {  
  17.         }  
  18.     }  
  19. }  
  20.   
  21.   
  22.   
  23. public class Host {  
  24.   
  25.     private final Helper helper = new Helper();  
  26.   
  27.     public void request(final int count, final char c) {  
  28.         System.out.println("reqeust (" + count + ", " + c + ") BEGIN");  
  29.         new Thread() {  
  30.             @Override  
  31.             public void run() {  
  32.                 helper.handle(count, c);  
  33.             }  
  34.         }.start();  
  35.         System.out.println("reqeust (" + count + ", " + c + ") END");  
  36.     }  
  37.   
  38.     public static void main(String[] args) {  
  39.         System.out.println("main Begin");  
  40.   
  41.         Host host = new Host();  
  42.         host.request(10'a');  
  43.         host.request(20'b');  
  44.         host.request(30'c');  
  45.   
  46.         System.out.println("main End");  
  47.     }  
  48.   
  49. }  

该模式适合在操作顺序无所谓的请求时。如果操作顺序有意义时,不适合适用Thread-Per-Message Pattern。另外不需要返回值的时候也是适合使用该模式的。

 


在该模式里,由于每个请求都需要启动一个线程,那么启动线程以及线程上下文的切换就成为了系统的瓶颈点,为了降低线程的启动所需的时间,可以使用Worker Thread Pattern



Worker Thread Pattern

     Woker Thread Pattern可以看作是Thread-Per-Message Pattern的改进,该模式定义了一个线程池,线程池里面的线程被称作Worker Thread。由于在系统启动时,这些Worker Thread已经准备好了,当请求来时,不需要在进行重现启动,并且系统中也维持了一定数量的Worker Thread,而不是不断的启动新线程,在性能上要优于Thread-Per-Message Pattern。
[java] view plain copy
 
  1. import java.util.Random;  
  2.   
  3. public class Request {  
  4.   
  5.     private final String        name;  
  6.   
  7.     private final int           number;  
  8.   
  9.     private final static Random random = new Random();  
  10.   
  11.     public Request(String name, int number) {  
  12.         this.name = name;  
  13.         this.number = number;  
  14.     }  
  15.   
  16.     public void request() {  
  17.         System.out.println(Thread.currentThread().getName() + " " + toString());  
  18.         try {  
  19.             Thread.sleep(random.nextInt(1000));  
  20.         } catch (InterruptedException e) {  
  21.         }  
  22.     }  
  23.   
  24.     @Override  
  25.     public String toString() {  
  26.         return "[ Reqeust name = " + name + ", number = " + number + " ]";  
  27.     }  
  28. }  
[java] view plain copy
 
  1. import java.util.LinkedList;  
  2.   
  3. public class Channel {  
  4.   
  5.     private final LinkedList<Request> buffers    = new LinkedList<Request>();  
  6.   
  7.     private static final int          bufferSize = 100;  
  8.   
  9.     private WorkerThread[]            threadPool;  
  10.   
  11.     public Channel(int threads) {  
  12.         this.threadPool = new WorkerThread[threads];  
  13.   
  14.         for (int i = 0; i < threads; i++) {  
  15.             threadPool[i] = new WorkerThread("WorkerThread-" + (i + 1), this);  
  16.         }  
  17.     }  
  18.   
  19.     public void startWorkers() {  
  20.         for (int i = 0; i < this.threadPool.length; i++) {  
  21.             threadPool[i].start();  
  22.         }  
  23.     }  
  24.   
  25.     public synchronized void put(Request request) throws InterruptedException {  
  26.         while (this.buffers.size() >= bufferSize) {  
  27.             wait();  
  28.         }  
  29.         this.buffers.addLast(request);  
  30.         notifyAll();  
  31.     }  
  32.   
  33.     public synchronized Request take() throws InterruptedException {  
  34.         while (this.buffers.size() == 0) {  
  35.             wait();  
  36.         }  
  37.         Request request = this.buffers.removeFirst();  
  38.         notifyAll();  
  39.         return request;  
  40.     }  
  41. }  
[java] view plain copy
 
  1. public class WorkerThread extends Thread {  
  2.   
  3.     private Channel channel;  
  4.   
  5.     public WorkerThread(String name, Channel channel) {  
  6.         super(name);  
  7.         this.channel = channel;  
  8.     }  
  9.   
  10.     @Override  
  11.     public void run() {  
  12.         while (true) {  
  13.             try {  
  14.                 Request request = this.channel.take();  
  15.                 request.request();  
  16.             } catch (InterruptedException e) {  
  17.             }  
  18.         }  
  19.     }  
  20. }  
[java] view plain copy
 
  1. package workerthread;  
  2.   
  3. import java.util.Random;  
  4.   
  5. public class ClientThread extends Thread {  
  6.   
  7.     private final Channel       channel;  
  8.   
  9.     private final static Random random = new Random();  
  10.   
  11.     public ClientThread(String name, Channel channel) {  
  12.         super(name);  
  13.         this.channel = channel;  
  14.     }  
  15.   
  16.     @Override  
  17.     public void run() {  
  18.         int i = 0;  
  19.         while (true) {  
  20.             Request request = new Request(getName(), ++i);  
  21.             try {  
  22.                 this.channel.put(request);  
  23.                 Thread.sleep(random.nextInt(1000));  
  24.             } catch (InterruptedException e) {  
  25.             }  
  26.         }  
  27.     }  
  28. }  
[java] view plain copy
 
  1. public class Main {  
  2.   
  3.     public static void main(String[] args) {  
  4.   
  5.         int threads = 5;  
  6.         Channel channel = new Channel(threads);  
  7.   
  8.         new ClientThread("Alice", channel).start();  
  9.         new ClientThread("Bobby", channel).start();  
  10.         new ClientThread("Chris", channel).start();  
  11.   
  12.         channel.startWorkers();  
  13.     }  
  14. }  

       Worker Thread Pattern 还将方法调用和方法执行进行了分离,所以我们在该模式里面看到了设计模式里面的Command Pattern的影子,因为它们的主题都是将方法调用和方法执行进行分离。方法调用和方法执行的分离可以提高响应性,能够控制实行的顺序,我们可以对 Reqeust设立优先性,控制Channel传递Request给Worker的顺序。同时我们可以取消方法的执行,或者重复方法的执行。
      Request对象可以进行多态。由于Worker Thread并不知道Request类的具体内容,只是知道执行Request类的execute方法而已,所以我们可以建立Request类的子类,并 将其实例传给Channel,Worker Thread也能正确调用这个实例的execute方法。
转载 blog.csdn.net/shenzhen_liubin/article/details/9825625

你可能感兴趣的:(java多线程)