转自:https://blog.csdn.net/dataiyangu/article/details/87123586

什么是设计模式
架构模式
设计模式
代码模式(成例 Idiom)
单例模式
普通单例
假如单例中有某个字段
改进的单例
代理模式再升级
不变模式
不变模式是如何实现的
不变模式的案例
Future模式
核心思想是异步调用
举个栗子
JDK对Future模式的支持
通过callable实现future
更加简便的方式实现future
生产者消费者
简单代码实现
什么是设计模式
在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题 ,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领 域引入到计算机科学的。
Richard Helm, Ralph Johnson ,John Vlissides (Gof) 和Gamma合称四人帮
《设计模式:可复用面向对象软件的基础》 收录 23种模式
– 观察者模式
– 策略模式
– 装饰者模式
– 享元模式
– 模板方法
– …

架构模式
– MVC
– 分层

设计模式
– 提炼系统中的组件

代码模式(成例 Idiom)
– 低层次,与编码直接相关
– 如DCL

class Person {
String name;
int birthYear;
byte[] raw;
public boolean equals(Object obj){
if (!obj instanceof Person)
return false;
Person other = (Person)obj;
return name.equals(other.name)
&& birthYear == other.birthYear
&& Arrays.equals(raw, other.raw);
}

public int hashCode(){
...

单例模式
单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样 有利于我们协调系统整体的行为
比如:全局信息配置
在多线程中通过单例模式,防止多个线程多次创建对象。

普通单例
public class Singleton {
private Singleton(){
System.out.println("Singleton is create");
}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
1
2
3
4
5
6
7
8
9
何时产生实例 不好控制

假如单例中有某个字段
一般来说,产生实例的时间是调用getinstance方法的时候,但是实际上是Singleton对象第一次被访问的时候。

public class Singleton {
public static int STATUS=1;
private Singleton(){
System.out.println("Singleton is create");
}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}

System.out.println(Singleton.STATUS);
1
Singleton is create 1
1
想要输出STATUS这个字段,去访问了Singleton这个类,会自动创建一个实例,这是一个不好的地方。

改进的单例
public class LazySingleton {
private LazySingleton() {
System.out.println("LazySingleton is create");
}
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance() {
if (instance == null)
instance = new LazySingleton();
return instance;
}
}

因为上面的一点小bug,所以衍生出了这种单例模式,只有是第一次(instance=null)的时候才会进行初始化,是一个延迟加载的过程,同时防止多线程进入此类而创建多个实例,所以在方法上加入了synchronized关键字,保证当有一个线程进来的时候,其他的线程进不来,所以只有一个线程能够进入if (instance == null)这句话,不会多个线程同时进行判断。但是synchronized这个锁,可能对于高并发,可能有点影响。

代理模式再升级
public class StaticSingleton {
private StaticSingleton(){
System.out.println("StaticSingleton is create");
}
private static class SingletonHolder {
private static StaticSingleton instance = new StaticSingleton();
}
public static StaticSingleton getInstance() {
return SingletonHolder.instance;
}
}

为了避免上面的synchronized带来的高并发的性能问题,衍生出了这种方式。
将new StaticSingleton放到内部类中,调用getInstance方法的时候再访问StaticSingleton类中的instance,再new StaticSingleton来进行初始化,如果有一个static的STATUS的变量的时候,去访问它,是不会创建本类的实例的,因为并没有对内部类进行初始化,所以,只有通过访问getInstance()这个方法的时候才会进行初始化。
通过这种方法也起到一种延迟加载的效果,而且没有高并发的性能问题,因为并没有加锁。

不变模式
一个类的内部状态创建后,在整个生命期间都不会发生变化时,就是不变类
不变模式不需要同步,因为不变模式是一个只读的对象。

不变模式是如何实现的
public final class Product {
//确保无子类
private final String no;
//私有属性,不会被其他对象获取
private final String name;
//final保证属性不会被2次赋值
private final double price;
public Product(String no, String name, double price) {//在创建对象时,必须指定数据
super();
//因为创建之后,无法进行修改 this.no = no;
this.name = name; this.price = price;
}
public String getNo() {
return no;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}

将类变成final的,保证没有子类,防止子类继承它,变成可变的。
将所有的属性变成final的,保证所有的字段只能被赋值一次。

不变模式的案例
java.lang.String
所有像是修改String的操作(replace,substring等),实际上是生成了一个新的String对象
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Short
以上所有的看似改变了原来对象的操作都是生成了一个新的对象。

Future模式
核心思想是异步调用
被集成在可jdk的开发包中,核心思想就是异步调用。

如上图更加清楚的阐述了这一过程,futuredate和realdata都继承自Data接口,函数调用的时候返回Data接口,而不管究竟是futuredate还是realdata,将futuredate(类似上面的订单,只是一个空壳)迅速的返回,然后等真正的数据构造完成之后再返回realdata,并且在futuredate中具有realdata的参数,来判断时候已经将真实的数据返回。

举个栗子
public interface Data {
public String getResult ();
}
1
2
3
public class FutureData implements Data {
protected RealData realdata = null;//FutureData是RealData的包装
protected boolean isReady = false;
public synchronized void setRealData(RealData realdata) {
if (isReady) {
return;
}
this.realdata = realdata; isReady = true; notifyAll();//RealData已经被注入,通知getResult()
}
public synchronized String getResult() {//会等待RealData构造完成
while (!isReady) {
try {
wait();//一直等待,知道RealData被注入
} catch (InterruptedException e) {
}
}
return realdata.result;//由RealData实现
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class RealData implements Data {
protected final String result;
public RealData(String para) {
//RealData的构造可能很慢,需要用户等待很久,这里使用sleep模拟
StringBuffer sb=new StringBuffer(); for (int i = 0; i < 10; i++) {
sb.append(para);
try {
//这里使用sleep,代替一个很慢的操作过程 Thread.sleep(100);
} catch (InterruptedException e) {
}
}
result =sb.toString();
}
public String getResult() {
return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Client {
public Data request(final String queryStr) {
final FutureData future = new FutureData();
new Thread() {
public void run() {// RealData的构建很慢,
//所以在单独的线程中进行
RealData realdata = new RealData(queryStr); future.setRealData(realdata);
}
}.start();
return future; // FutureData会被立即返回
}
}
1
2
3
4
5
6
7
8
9
10
11
12
重新开启一个线程进行setRealData,但是立即返回future,供使用。

public static void main(String[] args) {
Client client = new Client(); //这里会立即返回,因为得到的是FutureData而不是RealData
Data data = client.request("name");
System.out.println("请求完毕");
try {
//这里可以用一个sleep代替了对其他业务逻辑的处理 //在处理这些业务逻辑的过程中,RealData被创建,从而充分利用了等待时间
Thread.sleep(2000);
} catch (InterruptedException e) {
}
//使用真实的数据
System.out.println("数据 = " + data.getResult());
}
1
2
3
如果刚执行了Data data = client.request(“name”)返回的并不是真正的数据,这个时候去getResult一定会出现阻塞,但是中间执行了sleep(2000)或做一些其他的事情,并不会影响其他的业务,在真正需要数据的时候,在getResult,能够瞬间返回真正需要的数据。

JDK对Future模式的支持

核心是FutureTask,一个带有Future功能的Runnable

通过callable实现future
这里通过implements Callable来实现future的功能。

public class RealData implements Callable<String> {
private String para;
public RealData(String para){
this.para=para;
}
@Override
public String call() throws Exception {
StringBuffer sb=new StringBuffer();
for (int i = 0; i < 10; i++) {
sb.append(para);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
return sb.toString();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class FutureMain {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//构造FutureTask
FutureTask<String> future = new FutureTask<String>(new RealData("a"));
ExecutorService executor = Executors.newFixedThreadPool(1);
//执行FutureTask,相当于上例中的 client.request("a") 发送请求
//在这里开启线程进行RealData的call()执行
executor.submit(future);
System.out.println("请求完毕");
try{ //这里依然可以做额外的数据操作,这里使用sleep代替其他业务逻辑的处理
Thread.sleep(2000);
} catch (InterruptedException e) {
}
//相当于data.getResult (),取得call()方法的返回值
//如果此时call()方法没有执行完成,则依然会等待
System.out.println("数据 = " + future.get());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
核心代码:
FutureTask future = new FutureTask(new RealData(“a”));
上面说到jdk中对future的支持,其核心就是FutureTask,这里构造FutureTask
executor.submit(future);
System.out.println("数据 = " + future.get());
这里如果,在submit和get之间没有其他的操作直接进行get还是会形成阻塞的。

更加简便的方式实现future
public class FutureMain2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(1);
//执行FutureTask,相当于上例中的 client.request("a") 发送请求
//在这里开启线程进行RealData的call()执行
Future<String> future=executor.submit(new RealData("a"));
System.out.println("请求完毕");
try {
//这里依然可以做额外的数据操作,这里使用sleep代替其他业务逻辑的处理
Thread.sleep(2000);
} catch (InterruptedException e) {
}
//相当于data.getResult (),取得call()方法的返回值
//如果此时call()方法没有执行完成,则依然会等待
System.out.println("数据 = " + future.get());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
核心代码:
Future future=executor.submit(new RealData(“a”));
System.out.println("数据 = " + future.get());
因为callable是能够有返回值的,所以能够直接得到future,进一步简化了操作。

生产者消费者
生产者-消费者模式是一个经典的多线程设计模式。它为多线程间的协作提供了良好的解决方案。 在生产者-消费者模式中,通常由两类线程,即若干个生产者线程和若干个消费者线程。生产者线 程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务。生产者和消费者之间则通 过共享内存缓冲区进行通信。

线程a需要知道线程b的存在,线程b需要知道线程a的存在,如果更换了名字呢?从软件工程的角度讲,一个模块,对外最好是被知道的越少越好,一无所知最好,意味着外部的程序不论怎么改,对我都是没有影响的,能很好的降低耦合性。

角色 作用
生产者 用于提交用户请求,提取用户任务,并装入内存缓冲区
消费者 在内存缓冲区中提取并处理任务
内存缓冲区 缓存生产者提交的任务或数据,供消费者使用
任务 生成者向内存缓冲区提交的数据结构。
Main 使用生产者和消费者的客户端
简单代码实现
while (isRunning) {
Thread.sleep(r.nextInt(SLEEPTIME));
data = new PCData(count.incrementAndGet()); //构造任务数据
System.out.println(data+" is put into queue");
if (!queue.offer(data, 2, TimeUnit.SECONDS)) {
//提交数据到缓冲区中
}
}
System.err.println("failed to put data:" + data);

while(true){
PCData data = queue.take();
//提取任务
if (null != data) {
int re = data.getData() * data.getData(); //计算平方
System.out.println(MessageFormat.format("{0}*{1}={2}",
data.getData(), re));
}
}

Java高并发程序设计学习笔记(七):并行设计模式的更多相关文章

  1. Java高并发程序设计学习笔记(十一):Jetty分析

    转自:https://blog.csdn.net/dataiyangu/article/details/87894253 new Server()初始化线程池QueuedThreadPoolexecu ...

  2. Java高并发程序设计学习笔记(十):并发调试和JDK8新特性

    转自:https://blog.csdn.net/dataiyangu/article/details/87631574 多线程调试的方法使用Eclipse进行多线程调试线程dump及分析分析死锁案例 ...

  3. Java高并发程序设计学习笔记(一):并行简介以及重要概念

    转自:https://blog.csdn.net/dataiyangu/article/details/86211544#_28 文章目录为什么需要并行?反对意见大势所趋几个重要的概念同步(synch ...

  4. Java高并发程序设计学习笔记(五):JDK并发包(各种同步控制工具的使用、并发容器及典型源码分析(Hashmap等))

    转自:https://blog.csdn.net/dataiyangu/article/details/86491786#2__696 1. 各种同步控制工具的使用1.1. ReentrantLock ...

  5. Java高并发程序设计学习笔记(三):Java内存模型和线程安全

    转自:https://blog.csdn.net/dataiyangu/article/details/86412704 原子性有序性可见性– 编译器优化– 硬件优化(如写吸收,批操作)Java虚拟机 ...

  6. Java高并发程序设计学习笔记(二):多线程基础

    转自:https://blog.csdn.net/dataiyangu/article/details/86226835# 什么是线程?线程的基本操作线程的基本操作新建线程调用run的一种方式调用ru ...

  7. Java高并发程序设计学习笔记(九):锁的优化和注意事项

    转自:https://blog.csdn.net/dataiyangu/article/details/87612028 锁优化的思路和方法减少锁持有时间减小锁粒度锁分离锁粗化举个栗子举个栗子锁消除虚 ...

  8. Java高并发程序设计学习笔记(六):JDK并发包(线程池的基本使用、ForkJoin)

    转自:https://blog.csdn.net/dataiyangu/article/details/86573222 1. 线程池的基本使用1.1. 为什么需要线程池1.2. JDK为我们提供了哪 ...

  9. Java高并发程序设计学习笔记(四):无锁

    转自:https://blog.csdn.net/dataiyangu/article/details/86440836#1__3 1. 无锁类的原理详解简介:1.1. CAS1.2. CPU指令2. ...

随机推荐

  1. java 8 中文API

    java 8 中文API 转 https://blog.csdn.net/gao_zhennan/article/details/72871202 java 1.6 帮助文档 中文链接:http:// ...

  2. 基于SAR对Linux资源的监控shell脚本

    #! /bin/bash ] # $# 传递给脚本或函数的参数个数 then 脚本名称 exit -; fi SLEEP_TIME=$ LOG=$ while true do #线程数 thread_ ...

  3. 在Android初次的前期学习中的二个小例子(2)

    Hello13:SQLite数据库 一.简述SQLite的概念和主要特性 SQLite是一个轻量级的关系型数据库,运算速度快,占用资源少,使用非常方便,支持SQL语法标准和数据库事务原则. 相对于Sh ...

  4. Python--多任务(多进程,多线程,协程)

    1.单核CPU实现“多任务”:(注意:这里的多任务假的,是轮训执行多个任务一段时间) 1)时间片轮转 2)优先级调度算法 2.并行:真的多任务执行(CPU核数>=任务数):即在某个时刻点上,有多 ...

  5. [ML] Load and preview large scale data

    Ref: [Feature] Preprocessing tutorial 主要是 “无量纲化” 之前的部分. 加载数据 一.大数据源 http://archive.ics.uci.edu/ml/ht ...

  6. java安装配置

    1.下载 https://www.oracle.com/technetwork/java/javase/downloads/index.html 2.配置环境变量 点击"新建" 变 ...

  7. Linux 查看磁盘空间 相关命令

    Linux 查看磁盘空间 相关命令 实际工作中,我们经常需要查看磁盘空间的使用情况,以防止磁盘空间不足,导致的系统崩溃或者服务异常等问题. 常用的磁盘空间查看命令如下: 1.查看磁盘空间的整体使用情况 ...

  8. java数据结构之ConcurrentHashMap

    大神博客:https://www.cnblogs.com/study-everyday/p/6430462.html https://baijiahao.baidu.com/s?id=16170899 ...

  9. VS开发】如何给console控制台程序更换应用程序图标

    [VS开发]如何给console控制台程序更换应用程序图标 标签:[VS开发] 实际上非常简单,就是增加一个图标资源,在资源视图里,然后修改其ID为IDC_MAINFRAME,然后编译生成即可! 20 ...

  10. Transformer详解

    0 简述 Transformer改进了RNN最被人诟病的训练慢的缺点,利用self-attention机制实现快速并行. 并且Transformer可以增加到非常深的深度,充分发掘DNN模型的特性,提 ...