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. linux中的常用压缩与解压缩命令

    linux中常用的压缩格式有   .zip   .gz   .bz2   .tar.gz   .tar.bz2 一..zip 1.命令格式 zip 压缩文件名 源文件名         (压缩文件到当 ...

  2. MySQL学习笔记之二---引擎介绍MyISAM VS InnoDB

    前言 MyISAM是MySQL的默认数据库引擎(5.5版之前),由早期的ISAM(Indexed Sequential Access Method:有索引的顺序访问方法)所改良.虽然性能极佳,但却有一 ...

  3. openSUSE Linux 忘记root密码的解决方法

    openSUSE Linux 忘记root密码的解决方法 : 对于大部分linux发行版本,忘记root密码的时候,是可以通过单用户模式来重设密码的. 如在redhat/fedora 下,可以通过在启 ...

  4. python:while 语句的使用方法

    while语句: count = 0 while True: print(count) count += 1 if count == 10: break 实例: 计算n!,若:n = 5:则:n! = ...

  5. ARP跨网段广播

    如果源主机和目的主机不在同一网段,ARP请求的广播帧无法穿过路由器,源主机如何与目标主机实现通信? 补充一下问题,避免因问题不完整而出现误解. ---------------------------- ...

  6. 利用Surprise包进行电影推荐

    Surprise(Simple Python Recommendation System Engine)是一款推荐系统库,是scikit系列中的一个.简单易用,同时支持多种推荐算法(基础算法.协同过滤 ...

  7. 4.Hadoop集群搭建之启动

    配置好Hadoop.HDFS.YARN.Zookeeper集群后,需要按照顺序启动. 前期准备 配置好Hadoop.HDFS.YARN.Zookeeper集群 先stop掉所有的hadoop服务 使用 ...

  8. Spark之 RDD

    简介 RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变.可分区.里面的元素可并行计算的集合. Resilien ...

  9. iOS中NSDate常用转换操作整合

    //当前时间格式化, 例:YYYY-MM-dd-EEEE-HH:mm:ss + (NSString *)getCurrentDataWithDateFormate:(NSString *)format ...

  10. c# ftp 判断目录是否存在和创建文件夹

    工作中项目一直使用的ftp上传日志文件出现了问题,新的服务器搭建好后,日志无法上传.正好来学习一下ftp. 程序中的流程是,一个计时器,每分钟检测配置文件中本地日志文件路径下有没有日志文件,如果有就上 ...