这里总结几种常用的并行程序设计方法,其中部分文字源自《Java程序性能优化》一书中,还有部分文字属于个人总结,如有不对,请大家指出讨论。

Future模式

一句话,将客户端请求的处理过程从同步改为异步,以便将客户端解放出来,在服务端程序处理期间可以去干点其他事情,最后再来取请求的结果。

好处在于整个调用过程中不需要等待,可以充分利用所有的时间片段,提高系统的响应速度。

JDK中已经内置实现了FutureTask,使用起来非常方便,同时还可以取消Future任务,或者设置Future任务的超时时间。

1)实现一个Callable接口,实现服务端的具体的业务逻辑计算:

public class RealData implements Callable<String> {
public String call() throws Exception {...}
}

2)定义FutureTask,提交执行,获取返回结果:

// 1. create future task
FutureTask<String> future = new FutureTask<String>(new RealData("aaa"));
// 2. submit future task
ExecutorService exexutor = Executors.newFixedThreadPool(1);
exector.submit(future);
// 3. do other thing
......
// 4. get result, waiting util call method finished
System.out.println("Result:" + future.get());

Master-Worker模式

由两类线程实现:Master线程负责接收和分发任务(将任务拆成一个个的子任务);Worker线程负责处理子任务,每个Worker线程只处理部分任务,所有Worker线程共同完成所有任务的处理。

好处在于能够将一个大的任务拆分成若干个小的任务,从而交给不同的Worker并行的进行处理,进而提高系统的吞吐量。另外,Client端一旦提交任务后,Master线程完成任务的接收和分发后立即返回,因此对客户端来说,整个过程也是异步进行的。

一般的实现思路如下:

1)Master中首先需要维护一个队列Queue,用于接收任务,同时维护一个所有Worker线程的threadMap,以及每个子任务对应的处理结果集resultMap,这里由于涉及到多线程同时访问resultMap,因此一般使用JDK中的ConcurrentHashMap实现;

2)Worker线程实现Runnable或继承Thread,通过Master中的Queue获取拆分后的子任务,并进行业务处理,并将处理结果设置到resultMap中以便Master获取到;

3)Main入口函数则负责客户端请求的提交(需要先进程拆解),以及通过Master获取各个Worker的结果后进行合并,最后返回给客户端完成处理过程。

Guarded Suspension模式

所谓“保护暂停”模式,核心思想在于仅当服务进程准备好时,才提供服务。

好处在于既能保证所有的客户端请求均不丢失,同时也避免了服务器由于同时处理太多的请求而崩溃的现象,有效降低系统的瞬时负载,有助于系统稳定性。

其实这种通过中间加一层Queue做缓冲的模式在工作中用的很多,类似“ClientThread -> Request Queue -> ServerThread”的情况比比皆是,只不过可能实际中我们往往会结合其他方法一起使用,例如:

1)将ClientThread和ServerThread均为多个,则变为经典的“生产者-消费者”模式;

2)如果将ServerThread拆为1个Master和多个Worker,则又是上面提到的“Master-Worker”模式;

3)如果处理的请求需要返回结果,那么又需要和FutureTask结合起来使用(即:客户端的请求中需要带上FutureData,并在ServerThread中为FutureData设置上RealData)。

不变模式

并发多线程程序中,当多线程对同一个对象进行读写操作时,为了确保对象数据的一致性和准确性,必须进行同步操作,而这正是对系统性能损失严重的地方。因此,为了提高并发并发程序的性能,我们可以创建一种不可改变的对象,使用过程中保持不变性。这就是所谓“不变”模式。Java中这种模式用的很广,如String、Boolean、Short、Integer、Long、Byte等。

好处在于通过回避问题而不是解决问题的态度来处理多线程并发访问控制,但缺点是只适用于对象创建后内部状态和数据不可发生变化的情况。

Java中不变模式的实现很简单,按照OO的思想,只需要满足以下几点即可:

1)将对象的所有属性设为private final的;

2)通过final修饰class确保类不可被继承;

3)去掉对象中的所有settXX方法;

4)有包含所有属性的构造函数用于创建对象。

生产者-消费者模式

生产者线程向内存缓冲区提交任务,消费者线程从内存缓冲区获取任务并进行处理。

好处在于将生产者线程和消费者线程进行解耦,优化系统整体结构,缓解性能瓶颈对系统性能的影响。

Java中,一般来说使用LinkedBlockingQueue作为上面说的“内存缓冲区”,它是阻塞型BlockingQueue的一种使用Link List的实现,它对头和尾采用两把不同的锁,与ArrayBlockingQueue相比提高了吞吐量,适合于实现“生产者-消费者”模式。实现的大致思路如下:

1)创建Producer类,实现run方法用于提交任务;

2)创建Consumer类,实现run方法用于处理任务;

3)Main函数中建立缓冲区,若干个生产者,若干个消费者,创建线程池并开始使这些线程工作起来。

Java并行程序设计模式小结的更多相关文章

  1. Java进阶7 并发优化2 并行程序设计模式

    Java进阶7 并发优化2 并行程序设计模式20131114 1.Master-worker模式 前面讲解了Future模式,并且使用了简单的FutureTask来实现并发中的Future模式.下面介 ...

  2. 【MPI学习6】MPI并行程序设计模式:具有不连续数据发送的MPI程序设计

    基于都志辉老师<MPI并行程序设计模式>第14章内容. 前面接触到的MPI发送的数据类型都是连续型的数据.非连续类型的数据,MPI也可以发送,但是需要预先处理,大概有两类方法: (1)用户 ...

  3. Java并发程序设计(二)Java并行程序基础

    Java并行程序基础 一.线程的生命周期 其中blocked和waiting的区别: 作者:赵老师链接:https://www.zhihu.com/question/27654579/answer/1 ...

  4. JAVA并行程序基础

    JAVA并行程序基础 一.有关线程你必须知道的事 进程与线程 在等待面向线程设计的计算机结构中,进程是线程的容器.我们都知道,程序是对于指令.数据及其组织形式的描述,而进程是程序的实体. 线程是轻量级 ...

  5. JAVA并行程序基础二

    JAVA并行程序基础二 线程组 当一个系统中,如果线程较多并且功能分配比较明确,可以将相同功能的线程放入同一个线程组里. activeCount()可获得活动线程的总数,由于线程是动态的只能获取一个估 ...

  6. JAVA并行程序基础一

    JAVA并行程序基础一 线程的状态 初始线程:线程的基本操作 1. 新建线程 新建线程只需要使用new关键字创建一个线程对象,并且用start() ,线程start()之后会执行run()方法 不要直 ...

  7. 第2章 Java并行程序基础(三)

    2.8 程序中的幽灵:隐蔽的错误 2.8.1 无提示的错误案例 以求两个整数的平均值为例.请看下面代码: int v1 = 1073741827; int v2 = 1431655768; Syste ...

  8. 【MPI学习2】MPI并行程序设计模式:对等模式 & 主从模式

    这里的内容主要是都志辉老师<高性能计算之并行编程技术——MPI并行程序设计> 书上有一些代码是FORTAN的,我在学习的过程中,将其都转换成C的代码,便于统一记录. 这章内容分为两个部分: ...

  9. 如何提高Java并行程序性能??

    在Java程序中,多线程几乎已经无处不在.与单线程相比,多线程程序的设计和实现略微困难,但通过多线程,我们却可以获得多核CPU带来的性能飞跃,从这个角度说,多线程是一种值得尝试的技术.那么如何写出高效 ...

随机推荐

  1. 【IIS】 网站优化

    [IIS] 网站优化 一. 从硬件入手,升级服务器的cpu,内存,硬盘 这是成本最低的方法,所以如果要优化,请先考虑下现有服务器的硬件能力是不是满足要求. 二. 从数据库入手 索引: 检查该建的索引建 ...

  2. 【微博SDK调用逻辑】微博SDK的调用逻辑,最好自己还是写一个例子,试一下!!!

    逻辑是这样的,谢谢给我讲东西的开发哥哥,嘻嘻~~~  1.点击微博登录,SDK会打开微博客户端,然后点击登陆(如果已经登录了会出现一个当前app跟微博交互的图片界面,然后提示“正在获取授权信息”,如果 ...

  3. web.py+mysql插入中文提示query = query.encode(charset) UnicodeEncodeError: 'latin-1' codec can't encode characters in position 86-100

    对于中文编码的问题,总会出现各种各样恶心的错误,还不知道应该怎么解决,首先,你从最开头就应该关注编码问题,尽量保证所有的编码方式都是一致的 用python+web.py+mysql来写程序,首先要保证 ...

  4. HttpClient请求发送的几种用法:

    /// <summary> /// HttpClient实现Post请求 /// </summary> static async void dooPost() { string ...

  5. Python全栈之路-----基础篇

    Python诞生 Python是著名的”龟叔“Guido van Rossum(吉多·范罗苏姆)在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言. Python语法很多来自C,但又受到 ...

  6. shell脚本学习--shell中的变量$

    $$ :Shell本身的PID(ProcessID) $! :Shell最后运行的后台Process的PID $? :最后运行的命令的结束代码(返回值) $- :使用Set命令设定的Flag一览 $* ...

  7. Spring IOC容器中注入bean

    一.基于schema格式的注入 1.基本的注入方式 (属性注入方式) 根据setXxx()方法进行依赖注入,Spring只会检查是否有setter方法,是否有对应的属性不做要求 <bean id ...

  8. throw er; Unhandled 'error' event Error: listen EADDRINUSE的解决方法

    先把错误贴出来,如下: 出现此问题的原因是端口被占用,解决方法如下: 查看程序执行用到的端口的运行情况,本例是端口号3000; 占用该端口3000的进程是node,pid为244156,用kill命令 ...

  9. 10个你必须掌握的Linux超酷VI命令技巧

    大部分Linux开发者对vi命令相当熟悉,可是遗憾的是,大部分开发者都只能掌握一些最常用的Linux vi命令,下面介绍的10个vi命令虽然很多不为人知,但是在实际应用中又能让你大大提高效率. 在使用 ...

  10. c#开发Mongo笔记第三篇

    今天主要测试了一下查询功能了,当然了主要还是为了让查询可以和我们平时使用的实体对象关联起来,并且 那些BsonDocument和Collection我们操作起来不是太方便的 还是首先定义了一个用户类, ...