Java多线程设计模式(三)
Read-Wirte Lock Pattern
- public class ReadWriteLock {
- /**
- * 正在读取的线程数
- */
- private int readingThreadsNumber;
- /**
- * 正在写入的线程数(最多为1)
- */
- private int writingThreadsNumber;
- /**
- * 等待写入的线程数
- */
- private int waitingWriteThreadsNumber;
- /**
- * 是否优先写入,true:优先写入;false:优先读取
- */
- private boolean preferWriter = true;
- public synchronized void readLock() throws InterruptedException {
- // 如果有线程正在写入或者优先写入时,有线程正在等待写入,读取线程则等待
- while (this.writingThreadsNumber > 0
- || (this.preferWriter && this.waitingWriteThreadsNumber > 0)) {
- wait();
- }
- this.readingThreadsNumber++;
- }
- public synchronized void readUnlock() throws InterruptedException {
- this.readingThreadsNumber--;
- this.preferWriter = true;
- notifyAll();
- }
- public synchronized void writeLock() throws InterruptedException {
- this.waitingWriteThreadsNumber++;
- // 如果有线程正在写入或者正在读取,当前写入线程等待
- try {
- while (this.writingThreadsNumber > 0 || this.readingThreadsNumber > 0) {
- wait();
- }
- } finally {
- this.waitingWriteThreadsNumber--;
- }
- this.writingThreadsNumber++;
- }
- public synchronized void writeUnlock() throws InterruptedException {
- this.writingThreadsNumber--;
- this.preferWriter = false;
- notifyAll();
- }
- }
- public class Data {
- private char[] buffer;
- private ReadWriteLock readWriteLock = new ReadWriteLock();
- public Data(int size) {
- this.buffer = new char[size];
- for (int i = 0; i < size; i++) {
- this.buffer[i] = '*';
- }
- }
- public char[] read() throws InterruptedException {
- try {
- readWriteLock.readLock();
- return doRead();
- } finally {
- readWriteLock.readUnlock();
- }
- }
- public void write(char c) throws InterruptedException {
- try {
- readWriteLock.writeLock();
- doWrite(c);
- } finally {
- readWriteLock.writeUnlock();
- }
- }
- private char[] doRead() {
- char[] newChars = new char[buffer.length];
- System.arraycopy(this.buffer, 0, newChars, 0, this.buffer.length);
- slowly();
- return newChars;
- }
- private void doWrite(char c) {
- for (int i = 0; i < this.buffer.length; i++) {
- this.buffer[i] = c;
- slowly();
- }
- }
- private void slowly() {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- }
- }
- import java.util.Random;
- public class ReaderThread extends Thread {
- private static final Random random = new Random();
- private final Data data;
- public ReaderThread(Data data) {
- this.data = data;
- }
- @Override
- public void run() {
- while (true) {
- try {
- char[] c = data.read();
- System.out.println(Thread.currentThread().getName() + "reads " + String.valueOf(c));
- Thread.sleep(random.nextInt(1000));
- } catch (InterruptedException e) {
- }
- }
- }
- }
- 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;
- }
- @Override
- public void run() {
- while (true) {
- char c = nextChar();
- try {
- 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;
- }
- }
- public class MainThread {
- public static void main(String[] args) {
- int bufferSize = 10;
- Data data = new Data(bufferSize);
- 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 ReaderThread(data).start();
- String filler1 = "abcdefghjklmnopqrstuvwxyz";
- String filler2 = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
- new WriterThread(data, filler1).start();
- new WriterThread(data, filler2).start();
- }
- }
单纯使用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。
- public class Helper {
- public void handle(int count, char c) {
- System.out.println("handle(" + count + ", " + c + ") BEGIN");
- for (int i = 0; i < count; i++) {
- System.out.print(c);
- slowly();
- }
- System.out.println("");
- System.out.println("handle( " + count + ", " + c + ") END");
- }
- private void slowly() {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- }
- }
- }
- public class Host {
- private final Helper helper = new Helper();
- public void request(final int count, final char c) {
- System.out.println("reqeust (" + count + ", " + c + ") BEGIN");
- new Thread() {
- @Override
- public void run() {
- helper.handle(count, c);
- }
- }.start();
- System.out.println("reqeust (" + count + ", " + c + ") END");
- }
- public static void main(String[] args) {
- System.out.println("main Begin");
- Host host = new Host();
- host.request(10, 'a');
- host.request(20, 'b');
- host.request(30, 'c');
- System.out.println("main End");
- }
- }
该模式适合在操作顺序无所谓的请求时。如果操作顺序有意义时,不适合适用Thread-Per-Message Pattern。另外不需要返回值的时候也是适合使用该模式的。
在该模式里,由于每个请求都需要启动一个线程,那么启动线程以及线程上下文的切换就成为了系统的瓶颈点,为了降低线程的启动所需的时间,可以使用Worker Thread Pattern
Worker Thread Pattern
Pattern可以看作是Thread-Per-Message Pattern的改进,该模式定义了一个线程池,线程池里面的线程被称作Worker
Thread。由于在系统启动时,这些Worker
Thread已经准备好了,当请求来时,不需要在进行重现启动,并且系统中也维持了一定数量的Worker
Thread,而不是不断的启动新线程,在性能上要优于Thread-Per-Message Pattern。
- import java.util.Random;
- public class Request {
- private final String name;
- private final int number;
- private final static Random random = new Random();
- public Request(String name, int number) {
- this.name = name;
- this.number = number;
- }
- public void request() {
- System.out.println(Thread.currentThread().getName() + " " + toString());
- try {
- Thread.sleep(random.nextInt(1000));
- } catch (InterruptedException e) {
- }
- }
- @Override
- public String toString() {
- return "[ Reqeust name = " + name + ", number = " + number + " ]";
- }
- }
- import java.util.LinkedList;
- public class Channel {
- private final LinkedList<Request> buffers = new LinkedList<Request>();
- private static final int bufferSize = 100;
- private WorkerThread[] threadPool;
- public Channel(int threads) {
- this.threadPool = new WorkerThread[threads];
- for (int i = 0; i < threads; i++) {
- threadPool[i] = new WorkerThread("WorkerThread-" + (i + 1), this);
- }
- }
- public void startWorkers() {
- for (int i = 0; i < this.threadPool.length; i++) {
- threadPool[i].start();
- }
- }
- public synchronized void put(Request request) throws InterruptedException {
- while (this.buffers.size() >= bufferSize) {
- wait();
- }
- this.buffers.addLast(request);
- notifyAll();
- }
- public synchronized Request take() throws InterruptedException {
- while (this.buffers.size() == 0) {
- wait();
- }
- Request request = this.buffers.removeFirst();
- notifyAll();
- return request;
- }
- }
- public class WorkerThread extends Thread {
- private Channel channel;
- public WorkerThread(String name, Channel channel) {
- super(name);
- this.channel = channel;
- }
- @Override
- public void run() {
- while (true) {
- try {
- Request request = this.channel.take();
- request.request();
- } catch (InterruptedException e) {
- }
- }
- }
- }
- package workerthread;
- import java.util.Random;
- public class ClientThread extends Thread {
- private final Channel channel;
- private final static Random random = new Random();
- public ClientThread(String name, Channel channel) {
- super(name);
- this.channel = channel;
- }
- @Override
- public void run() {
- int i = 0;
- while (true) {
- Request request = new Request(getName(), ++i);
- try {
- this.channel.put(request);
- Thread.sleep(random.nextInt(1000));
- } catch (InterruptedException e) {
- }
- }
- }
- }
- public class Main {
- public static void main(String[] args) {
- int threads = 5;
- Channel channel = new Channel(threads);
- new ClientThread("Alice", channel).start();
- new ClientThread("Bobby", channel).start();
- new ClientThread("Chris", channel).start();
- channel.startWorkers();
- }
- }
Worker Thread Pattern
还将方法调用和方法执行进行了分离,所以我们在该模式里面看到了设计模式里面的Command
Pattern的影子,因为它们的主题都是将方法调用和方法执行进行分离。方法调用和方法执行的分离可以提高响应性,能够控制实行的顺序,我们可以对
Reqeust设立优先性,控制Channel传递Request给Worker的顺序。同时我们可以取消方法的执行,或者重复方法的执行。
Thread并不知道Request类的具体内容,只是知道执行Request类的execute方法而已,所以我们可以建立Request类的子类,并
将其实例传给Channel,Worker Thread也能正确调用这个实例的execute方法。
Java多线程设计模式(三)的更多相关文章
- [温故]图解java多线程设计模式(一)
去年看完的<图解java多线程设计模式>,可惜当时没做笔记,导致后来忘了许多东西,打算再温习下这本书,顺便在这里记录一下~ 1.顺序执行.并行.并发 顺序执行:多个操作按照顺序依次执行. ...
- Java多线程的三种实现方式
java多线程的三种实现方式 一.继承Thread类 二.实现Runnable接口 三.使用ExecutorService, Callable, Future 无论是通过继承Thread类还是实现Ru ...
- java多线程设计模式
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt220 java多线程设计模式 java语言已经内置了多线程支持,所有实现Ru ...
- Java多线程(三)如何创建线程
点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...
- “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第三十六章:如何设置线程的等待截止时间
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 下一章 "全栈2019"J ...
- “全栈2019”Java多线程第三十五章:如何获取线程被等待的时间?
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第三十四章:超时自动唤醒被等待的线程
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第三十三章:await与signal/signalAll
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
随机推荐
- 1.Linux下生成密钥
1.Linux下生成密钥 ssh-keygen的命令手册,通过”man ssh-keygen“命令: 通过命令”ssh-keygen -t rsa“ 生成之后会在用户的根目录生成一个 “.ssh”的文 ...
- SVN更新或提交时出现冲突该如何解决
解决版本冲突的命令.在冲突解决之后,需要使用svnresolved来告诉subversion冲突解决,这样才能提交更新.冲突发生时,subversion会在WorkCopy中保存所有的目标文件版本(上 ...
- 25行 Python 代码实现人脸检测——OpenCV 技术教程
这是篇是利用 OpenCV 进行人脸识别的技术讲解.阅读本文之前,这是注意事项: 建议先读一遍本文再跑代码——你需要理解这些代码是干什么的.成功跑一遍不是目的,能够举一反三.在新任务上找出 bug 才 ...
- Mysql总结(二)
数据库.表.字段.行 问:查询姓黄或洪的男生分析:数据从哪来,哪个表stu条件:姓黄或洪name or and 男生gender答:select * from stu where gender=1 a ...
- [转] C# 获取程序运行目录
来自 莫等闲也,原文 // 获取程序的基目录. System.AppDomain.CurrentDomain.BaseDirectory // 获取模块的完整路径. System.Diagnosti ...
- C++Primer笔记-----day05
=======================================================================day05======================== ...
- 解决Eclipse编辑JavaScript时卡的问题
eclipse在开发JavaEE项目时容易卡,特别是在编辑JavaScript时,经过网上各种搜索,综合整理一下,对自己的eclipse设置之后,结果不在出现卡的问题了. 原文地址:http://bl ...
- 使用net.sf.fjep.fatjar插件将第三方JAR包打包进自已的JAR包中
一般单个工程,在没有应用别人的jar包时导出为jar很简单,只要设置一个Main-Class就行了,也就是选择程序入口(main所在类).但是涉及到了数据库或需要用到第三方的JAR,就需要用到相应的数 ...
- IE WebDeveloper--IE浏览器web调试工具
目前市面上比较火爆的浏览器内核提供商,有微软的IE.mozilla的firefox.谷歌的chrome.苹果的safari.IE浏览器下的项目过去占比非常大,近年随着其他浏览器厂商发展势头迅猛,过去的 ...
- linux tar 压缩
压缩文件 tar -czvf xxx.tar.gz yourdict 解压文件 tar xzf aa.tar.gz