Read-Wirte Lock Pattern

        Read-Write Lock Pattern 将读取和写入分开来处理。在读取数据之前,必须获取用来读取的锁定。而要写入的时候,则必须获取用来写入的锁定。因为进行读取时,实例的状态不会改变,所 以,就算有多个线程在同时读取也没有关系。但当有线程在进行写入的时候,不可以再进行写入的操作。写入的时候,实例的状态会改变。于是,当有一个线程在写 入的时候,其他线程就不可以进行读取或写入。一般来说,进行共享互斥会使程序性能变差,但将写入的共享互斥与读取的共享互斥拆分开来,就可以提高程序的性 能
  1. public class ReadWriteLock {
  2. /**
  3. * 正在读取的线程数
  4. */
  5. private int     readingThreadsNumber;
  6. /**
  7. * 正在写入的线程数(最多为1)
  8. */
  9. private int     writingThreadsNumber;
  10. /**
  11. * 等待写入的线程数
  12. */
  13. private int     waitingWriteThreadsNumber;
  14. /**
  15. * 是否优先写入,true:优先写入;false:优先读取
  16. */
  17. private boolean preferWriter = true;
  18. public synchronized void readLock() throws InterruptedException {
  19. // 如果有线程正在写入或者优先写入时,有线程正在等待写入,读取线程则等待
  20. while (this.writingThreadsNumber > 0
  21. || (this.preferWriter && this.waitingWriteThreadsNumber > 0)) {
  22. wait();
  23. }
  24. this.readingThreadsNumber++;
  25. }
  26. public synchronized void readUnlock() throws InterruptedException {
  27. this.readingThreadsNumber--;
  28. this.preferWriter = true;
  29. notifyAll();
  30. }
  31. public synchronized void writeLock() throws InterruptedException {
  32. this.waitingWriteThreadsNumber++;
  33. // 如果有线程正在写入或者正在读取,当前写入线程等待
  34. try {
  35. while (this.writingThreadsNumber > 0 || this.readingThreadsNumber > 0) {
  36. wait();
  37. }
  38. } finally {
  39. this.waitingWriteThreadsNumber--;
  40. }
  41. this.writingThreadsNumber++;
  42. }
  43. public synchronized void writeUnlock() throws InterruptedException {
  44. this.writingThreadsNumber--;
  45. this.preferWriter = false;
  46. notifyAll();
  47. }
  48. }
  1. public class Data {
  2. private char[]        buffer;
  3. private ReadWriteLock readWriteLock = new ReadWriteLock();
  4. public Data(int size) {
  5. this.buffer = new char[size];
  6. for (int i = 0; i < size; i++) {
  7. this.buffer[i] = '*';
  8. }
  9. }
  10. public char[] read() throws InterruptedException {
  11. try {
  12. readWriteLock.readLock();
  13. return doRead();
  14. } finally {
  15. readWriteLock.readUnlock();
  16. }
  17. }
  18. public void write(char c) throws InterruptedException {
  19. try {
  20. readWriteLock.writeLock();
  21. doWrite(c);
  22. } finally {
  23. readWriteLock.writeUnlock();
  24. }
  25. }
  26. private char[] doRead() {
  27. char[] newChars = new char[buffer.length];
  28. System.arraycopy(this.buffer, 0, newChars, 0, this.buffer.length);
  29. slowly();
  30. return newChars;
  31. }
  32. private void doWrite(char c) {
  33. for (int i = 0; i < this.buffer.length; i++) {
  34. this.buffer[i] = c;
  35. slowly();
  36. }
  37. }
  38. private void slowly() {
  39. try {
  40. Thread.sleep(100);
  41. } catch (InterruptedException e) {
  42. }
  43. }
  44. }
  1. import java.util.Random;
  2. public class ReaderThread extends Thread {
  3. private static final Random random = new Random();
  4. private final Data          data;
  5. public ReaderThread(Data data) {
  6. this.data = data;
  7. }
  8. @Override
  9. public void run() {
  10. while (true) {
  11. try {
  12. char[] c = data.read();
  13. System.out.println(Thread.currentThread().getName() + "reads " + String.valueOf(c));
  14. Thread.sleep(random.nextInt(1000));
  15. } catch (InterruptedException e) {
  16. }
  17. }
  18. }
  19. }
  1. import java.util.Random;
  2. public class WriterThread extends Thread {
  3. private static final Random random = new Random();
  4. private final Data          data;
  5. private final String        filler;
  6. private int                 index  = 0;
  7. public WriterThread(Data data, String filler) {
  8. this.data = data;
  9. this.filler = filler;
  10. }
  11. @Override
  12. public void run() {
  13. while (true) {
  14. char c = nextChar();
  15. try {
  16. data.write(c);
  17. Thread.sleep(random.nextInt(1000));
  18. } catch (InterruptedException e) {
  19. }
  20. }
  21. }
  22. private char nextChar() {
  23. char c = filler.charAt(index);
  24. index++;
  25. if (index > filler.length()) {
  26. index = 0;
  27. }
  28. return c;
  29. }
  30. }
  1. public class MainThread {
  2. public static void main(String[] args) {
  3. int bufferSize = 10;
  4. Data data = new Data(bufferSize);
  5. new ReaderThread(data).start();
  6. new ReaderThread(data).start();
  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. String filler1 = "abcdefghjklmnopqrstuvwxyz";
  13. String filler2 = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
  14. new WriterThread(data, filler1).start();
  15. new WriterThread(data, filler2).start();
  16. }
  17. }

单纯使用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。

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

该模式适合在操作顺序无所谓的请求时。如果操作顺序有意义时,不适合适用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。
  1. import java.util.Random;
  2. public class Request {
  3. private final String        name;
  4. private final int           number;
  5. private final static Random random = new Random();
  6. public Request(String name, int number) {
  7. this.name = name;
  8. this.number = number;
  9. }
  10. public void request() {
  11. System.out.println(Thread.currentThread().getName() + " " + toString());
  12. try {
  13. Thread.sleep(random.nextInt(1000));
  14. } catch (InterruptedException e) {
  15. }
  16. }
  17. @Override
  18. public String toString() {
  19. return "[ Reqeust name = " + name + ", number = " + number + " ]";
  20. }
  21. }
  1. import java.util.LinkedList;
  2. public class Channel {
  3. private final LinkedList<Request> buffers    = new LinkedList<Request>();
  4. private static final int          bufferSize = 100;
  5. private WorkerThread[]            threadPool;
  6. public Channel(int threads) {
  7. this.threadPool = new WorkerThread[threads];
  8. for (int i = 0; i < threads; i++) {
  9. threadPool[i] = new WorkerThread("WorkerThread-" + (i + 1), this);
  10. }
  11. }
  12. public void startWorkers() {
  13. for (int i = 0; i < this.threadPool.length; i++) {
  14. threadPool[i].start();
  15. }
  16. }
  17. public synchronized void put(Request request) throws InterruptedException {
  18. while (this.buffers.size() >= bufferSize) {
  19. wait();
  20. }
  21. this.buffers.addLast(request);
  22. notifyAll();
  23. }
  24. public synchronized Request take() throws InterruptedException {
  25. while (this.buffers.size() == 0) {
  26. wait();
  27. }
  28. Request request = this.buffers.removeFirst();
  29. notifyAll();
  30. return request;
  31. }
  32. }
  1. public class WorkerThread extends Thread {
  2. private Channel channel;
  3. public WorkerThread(String name, Channel channel) {
  4. super(name);
  5. this.channel = channel;
  6. }
  7. @Override
  8. public void run() {
  9. while (true) {
  10. try {
  11. Request request = this.channel.take();
  12. request.request();
  13. } catch (InterruptedException e) {
  14. }
  15. }
  16. }
  17. }
  1. package workerthread;
  2. import java.util.Random;
  3. public class ClientThread extends Thread {
  4. private final Channel       channel;
  5. private final static Random random = new Random();
  6. public ClientThread(String name, Channel channel) {
  7. super(name);
  8. this.channel = channel;
  9. }
  10. @Override
  11. public void run() {
  12. int i = 0;
  13. while (true) {
  14. Request request = new Request(getName(), ++i);
  15. try {
  16. this.channel.put(request);
  17. Thread.sleep(random.nextInt(1000));
  18. } catch (InterruptedException e) {
  19. }
  20. }
  21. }
  22. }
  1. public class Main {
  2. public static void main(String[] args) {
  3. int threads = 5;
  4. Channel channel = new Channel(threads);
  5. new ClientThread("Alice", channel).start();
  6. new ClientThread("Bobby", channel).start();
  7. new ClientThread("Chris", channel).start();
  8. channel.startWorkers();
  9. }
  10. }

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多线程设计模式(三)的更多相关文章

  1. [温故]图解java多线程设计模式(一)

    去年看完的<图解java多线程设计模式>,可惜当时没做笔记,导致后来忘了许多东西,打算再温习下这本书,顺便在这里记录一下~  1.顺序执行.并行.并发 顺序执行:多个操作按照顺序依次执行. ...

  2. Java多线程的三种实现方式

    java多线程的三种实现方式 一.继承Thread类 二.实现Runnable接口 三.使用ExecutorService, Callable, Future 无论是通过继承Thread类还是实现Ru ...

  3. java多线程设计模式

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt220 java多线程设计模式 java语言已经内置了多线程支持,所有实现Ru ...

  4. Java多线程(三)如何创建线程

    点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...

  5. “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. “全栈2019”Java多线程第三十六章:如何设置线程的等待截止时间

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 下一章 "全栈2019"J ...

  7. “全栈2019”Java多线程第三十五章:如何获取线程被等待的时间?

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  8. “全栈2019”Java多线程第三十四章:超时自动唤醒被等待的线程

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  9. “全栈2019”Java多线程第三十三章:await与signal/signalAll

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  10. “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

随机推荐

  1. python学习笔记(九):操作数据库

    我们在写代码的时候,经常会操作数据库,增删改查,数据库有很多类型,关系型数据库和非关系数据库,这里咱们介绍一下python怎么操作mysql.redis和mongodb. 一.python操作mysq ...

  2. Fastq 常用软件

    文章转载于 Original 2017-06-08 Jolvii 生信百科 由于生物信息的大部分工作都是在没有 root 权限的集群上进行的,本期我主要介绍一下非 root 用户怎么安装常用的软件.工 ...

  3. 代做JSP课程设计,毕业设计

    代做JSP课程设计,毕业设计,大家都是学生,绝对靠谱,有意者加我Q 279283855

  4. C#读取Excel技术概览 (2)

    5.自定义SDK,使用xmlReader文件流式处理 第四章节中,总是感觉用别人的工具要受制于人.既然我 们知道了Excel的存储方式,问题便转换成从xml中取出数据,然后放入内存得到我们想要的东西, ...

  5. zt对于C#中的FileUpload解决文件上传大小限制的问题设置

    对于C#中的FileUpload解决文件上传大小限制的问题设置 您可能没意识到,但对于可以使用该技术上载的文件的大小存在限制.默认情况下,使用 FileUpload 控件上载到服务器的文件最大为 4M ...

  6. C#自省

    [C#自省] 1.根据string,获取type.Type.GetType 方法,获取具有指定名称的 Type,执行区分大小写的搜索. 2.根据obj,获取type.Object.GetType 方法 ...

  7. 我的MBTI性格测试

    写在前面: 很多人争论MBTI靠谱不靠谱.一个人的性格肯定不能只用这么几个维度就能描述的,一个人的性格也肯定不是通过这么几个问题就能测出来的,一个人的性格也肯定不是一成不变的,所以MBTI的准确度肯定 ...

  8. Nginx 相关介绍(Nginx是什么?能干嘛?个人觉得写得比较好的文章,转载过来)

    Nginx的产生 没有听过Nginx?那么一定听过它的"同行"Apache吧!Nginx同Apache一样都是一种WEB服务器.基于REST架构风格,以统一资源描述符(Unifor ...

  9. $(window).load()和$(document).ready()

    一.前言 我们在编写前端代码的js文件时,往往是会先写一个$(function(){}),然后才会在大括号里面继续写我们自己的代码.当时并不能理解为什么要添加这样一个东西,只是把它当做一个标签一样添加 ...

  10. make: *** No rule to make target `build', needed by `default'. Stop.

    [root@xx nginx-1.8.0]# makemake: *** No rule to make target `build', needed by `default'.  Stop. [ro ...