Java多线程设计模式(3)

《Java多线程设计模式》读书笔记3

目录:
1 Thread-Per-Message Pattern
2 Worker Thread Pattern
3 Future Pattern

===================Thread-Per-Message Pattern===============

Thread per message,每个消息一个线程。Message 在这里可以看作是“命令”或“请求”的意思。对每个命令或请求,分配一个线程,有这个线程执行工作,这就是Thread-Per-Message Pattern。

Thread-Per-Message Pattern的适用场合:
1. 适合在操作顺序无所谓时使用。
2. 在不需要返回值的时候。
3. 可以应用在服务器的制作上,提升响应性,降低延迟时间。

进程和线程
1 进程和线程最大的差异在于内存能否共享。
通常每个进程所拥有的内存空间是各自独立的。进程不能擅自读取,改写其他进程的内存空间。因为进程的内存空间是相互独立的,所以进程无需担心被其他进程破坏的危险。而线程则是共享内存的。
2 进程和线程另一个差异,在于context-switch的频繁程度。
进程切换需要存储和保留的信息比较多,所以切换需要花费一些时间。然而线程需要管理的context信息比进程要少得多,所以一般而言线程context-switch比进程的context-switch要快的多。

===================Worker Thread Pattern===============

线程池机制 主要解决每次请求都要建立新线程的成本。事先启动用来执行工作的线程(工人线程)备用。并使用Producer-Consumer Pattern,将表示工作内容的实例传递给工人线程。这么一来,工人线程会负责执行工作,就不需要一直启动新的线程了。
实现:

工作区(核心)
public class Channel { 
  //允许最多请求数 
 private static final int MAX_REQUEST = 100; 
  //请求容器 
 private final Request[] requestQueue; 
  private int tail; // 下一个putRequest的地方 
  private int head; // 下一个takeRequest的地方 
  private int count; // Request的数量 
  //工作线程组 
  private final WorkerThread[] threadPool; 
  public Channel(int threads) { 
    //初始请求容器 
   this.requestQueue = new Request[MAX_REQUEST]; 
    this.head = 0; 
    this.tail = 0; 
    this.count = 0; 
     //初始化工作线程组 
    threadPool = new WorkerThread[threads]; 
    for (int i = 0; i < threadPool.length; i++) { 
      threadPool[i] = new WorkerThread("Worker-" + i, this); 
    } 
  } 
  //工作线程开始工作 
  public void startWorkers() { 
    for (int i = 0; i < threadPool.length; i++) { 
      threadPool[i].start(); 
    } 
  } 
  //添加请求 
  public synchronized void putRequest(Request request) { 
    //当容器缓冲请求数大于容器容量时等待 
   while (count >= requestQueue.length) { 
      try { 
        wait(); 
      } catch (InterruptedException e) { 
      } 
    } 
    requestQueue[tail] = request; 
    //获取下一次放入请求的位置 
    tail = (tail + 1) % requestQueue.length; 
    count++; 
    notifyAll(); 
  } 
  //处理请求 
  public synchronized Request takeRequest() { 
    //当请求数为空时等待 
   while (count <= 0) { 
      try { 
        wait(); 
      } catch (InterruptedException e) { 
      } 
    } 
    Request request = requestQueue[head]; 
    //获取下一次获取请求的位置 
    head = (head + 1) % requestQueue.length; 
    count--; 
    notifyAll(); 
    return request; 
  } 
}

请求对象
public class Request { 
  private final String name; // 委托者 
  private final int number; // 请求编号 
  private static final Random random = new Random(); 
  public Request(String name, int number) { 
    this.name = name; 
    this.number = number; 
  } 
  public void execute() { 
    System.out.println(Thread.currentThread().getName() + " executes " + this); 
    try { 
      Thread.sleep(random.nextInt(1000)); 
    } catch (InterruptedException e) { 
    } 
  } 
  public String toString() { 
    return "[ Request from " + name + " No." + number + " ]"; 
  } 
}
工作线程
public class WorkerThread extends Thread { 
  private final Channel channel; 
  public WorkerThread(String name, Channel channel) { 
    super(name); 
    this.channel = channel; 
  } 
  public void run() { 
    while (true) { 
      Request request = channel.takeRequest(); 
      request.execute(); 
    } 
  } 
}
放入请求线程
public class ClientThread extends Thread { 
  private final Channel channel; 
  private static final Random random = new Random(); 
  public ClientThread(String name, Channel channel) { 
    super(name); 
    this.channel = channel; 
  } 
  public void run() { 
    try { 
      for (int i = 0; true; i++) { 
        Request request = new Request(getName(), i); 
        channel.putRequest(request); 
        Thread.sleep(random.nextInt(1000)); 
      } 
    } catch (InterruptedException e) { 
    } 
  } 
}
测试类
public class Main { 
  public static void main(String[] args) { 
    Channel channel = new Channel(5);  // 工作线程的數量 
    channel.startWorkers(); 
    new ClientThread("Alice", channel).start(); 
    new ClientThread("Bobby", channel).start(); 
    new ClientThread("Chris", channel).start(); 
  } 
}

===================Future Pattern===============

Future是“未来”,“期货”的意思。假设有一个执行起来需要花一些时间的方法,我们就不要等待执行结果出来了,而获取一张替的“提货单”。因为获取提货单不需要花时间,这时这个“提货单”就是Future参与者。获取Future参与者的线程,会在事后再去获取执行结果。就好像那提货单去领取蛋糕一样,如果已经有执行结果了,就可以马上拿到数据。如果执行结果还没好,则继续等待到执行结果出现为止。
Thread-Per-Message Pattern是将花费时间的工作交给别的线程,以提高程序的响应性。不过,如果我们需要得到别的线程所处理的结果时,就行不通了。若同步执行需要花一些时间的操作,会使程序响应性降低。但是,如果异步的开始执行,却无法在第一时间得知结果。这种时候就要使用Future Pattern。首先我们建立一个与处理结果具有相同接口(API)的Future参与者。接着,在开始处理时,先把Future参与者当作返回值返回。直到其他线程处理完以后,才将真正的结果设置给Future参与者。Client参与者可以通过Future参与者得到处理的结果。使用这个Pattern,可使响应性不降低,并得到想要的处理结果。

原文地址:https://www.cnblogs.com/gaojing/p/1755645.html