`

构建Java并发模型框架

阅读更多
关键字: 构建java并发模型框架
Java的多线程特性为构建高性能的应用提供了极大的方便,但是也带来了不少的麻烦。线程间同步、数据一致性等烦琐的问题需要细 心的考虑,一不小心就会出现一些微妙的,难以调试的错误。另外,应用逻辑和线程逻辑纠缠在一起,会导致程序的逻辑结构混乱,难以复用和维护。本文试图给出 一个解决这个问题的方案,通过构建一个并发模型框架(framework),使得开发多线程的应用变得容易。

基础知识

Java 语言提供了对于线程很好的支持,实现方法小巧、优雅。对于方法重入的保护,信号量(semaphore)和临界区(critical section)机制的实现都非常简洁。可以很容易的实现多线程间的同步操作从而保护关键数据的一致性。这些特点使得Java成为面向对象语言中对于多线 程特性支持方面的佼佼者(C++正在试图把boost库中的对于线程的支持部分纳入语言标准)。
Java中内置 了对于对象并发访问的支持,每一个对象都有一个监视器(monitor),同时只允许一个线程持有监视器从而进行对对象的访问,那些没有获得监视器的线程 必须等待直到持有监视器的线程释放监视器。对象通过synchronized关键字来声明线程必须获得监视器才能进行对自己的访问。
synchronized 声明仅仅对于一些较为简单的线程间同步问题比较有效,对于哪些复杂的同步问题,比如带有条件的同步问题,Java提供了另外的解决方 法,wait/notify/notifyAll。获得对象监视器的线程可以通过调用该对象的wait方法主动释放监视器,等待在该对象的线程等待队列 上,此时其他线程可以得到监视器从而访问该对象,之后可以通过调用notify/notifyAll方法来唤醒先前因调用wait方法而等待的线程。一般 情况下,对于wait/notify/notifyAll方法的调用都是根据一定的条件来进行的,比如:经典的生产者/消费者问题中对于队列空、满的判 断。熟悉POSIX的读者会发现,使用wait/notify/notifyAll可以很容易的实现POSIX中的一个线程间的高级同步技术:条件变量。
有很多的书籍、资料对于synchronized、wait/notify/notifyAll进行了详细的介绍,参考文献〔3〕中对于synchronized关键字以及和线程有关的Java内存模型有深入详细的论述,有兴趣的读者可以自行学习,不在此赘述。



简单例子

本文将围绕一个简单的例子展开论述,这样可以更容易突出我们解决问题的思路、方法。本文想向读者展现的正是这些思路、方法。这些思路、方法更加适用于解决大规模、复杂应用中的并发问题。
考虑一个简单的例子,我们有一个服务提供者,它通过一个接口对外提供服务,服务内容非常简单,就是在标准输出上打印Hello World。类结构图如下:


代码如下:
Java代码
interface Service 

    public void sayHello(); 

class ServiceImp implements Service 

    public void sayHello() { 
        System.out.println("Hello World!"); 
    } 

class Client 

    public Client(Service s) { 
        _service = s; 
}     
    public void requestService() { 
        _service.sayHello(); 
    } 
    private Service _service; 



如果现在有新的需求,要求该服务必须支持 Client的并发访问。一种简单的方法就是在ServicImp类中的每个方法前面加上synchronized声明,来保证自己内部数据的一致性(当 然对于本例来说,目前是没有必要的,因为ServiceImp没有需要保护的数据,但是随着需求的变化,以后可能会有的)。但是这样做至少会存在以下几个 问题:
现在要维护ServiceImp的两个版本:多线程版本和单线程版本(有些地方,比如其他项目,可能没有并发的问题),容易带来同步更新和正确选择版本的问题,给维护带来麻烦。
如果多个并发的Client频繁调用该服务,由于是直接同步调用,会造成Client阻塞,降低服务质量。
很难进行一些灵活的控制,比如:根据Client的优先级进行排队等等。
这些问题对于大型的多线程应用服务器尤为突出,对于一些简单的应用(如本文中的例子)可能根本不用考虑。本文正是要讨论这些问题的解决方案,文中的简单的例子只是提供了一个说明问题,展示思路、方法的平台。
如何才能较好的解决这些问题,有没有一个可以重用的解决方案呢?让我们先把这些问题放一放,先来谈谈和框架有关的一些问题。



框架概述

熟悉 面向对象的读者一定知道面向对象的最大的优势之一就是:软件复用。通过复用,可以减少很多的工作量,提高软件开发生产率。复用本身也是分层次的,代码级的复用和设计架构的复用。
大 家可能非常熟悉C语言中的一些标准库,它们提供了一些通用的功能让你的程序使用。但是这些标准库并不能影响你的程序结构和设计思路,仅仅是提供一些机能, 帮助你的程序完成工作。它们使你不必重头编写一般性的通用功能(比如printf),它们强调的是程序代码本身的复用性,而不是设计架构的复用性。
那 么什么是框架呢?所谓框架,它不同于一般的标准库,是指一组紧密关联的(类)classes,强调彼此的配合以完成某种可以重复运用的设计概念。这些类之 间以特定的方式合作,彼此不可或缺。它们相当程度的影响了你的程序的形貌。框架本身规划了应用程序的骨干,让程序遵循一定的流程和动线,展现一定的风貌和 功能。这样就使程序员不必费力于通用性的功能的繁文缛节,集中精力于专业领域。
有一点必须要强调,放之四海而皆 准的框架是不存在的,也是最没有用处的。框架往往都是针对某个特定应用领域的,是在对这个应用领域进行深刻理解的基础上,抽象出该应用的概念模型,在这些 抽象的概念上搭建的一个模型,是一个有形无体的框架。不同的具体应用根据自身的特点对框架中的抽象概念进行实现,从而赋予框架生命,完成应用的功能。
基 于框架的应用都有两部分构成:框架部分和特定应用部分。要想达到框架复用的目标,必须要做到框架部分和特定应用部分的隔离。使用面向对象的一个强大功能: 多态,可以实现这一点。在框架中完成抽象概念之间的交互、关联,把具体的实现交给特定的应用来完成。其中一般都会大量使用了Template Method设计模式。
Java中的Collection Framework以及微软的MFC都是框架方面很好的例子。有兴趣的读者可以自行研究。



构建框架

如 何构建一个Java并发模型框架呢?让我们先回到原来的问题,先来分析一下原因。造成要维护多线程和单线程两个版本的原因是由于把应用逻辑和并发逻辑混在 一起,如果能够做到把应用逻辑和并发模型进行很好的隔离,那么应用逻辑本身就可以很好的被复用,而且也很容易把并发逻辑添加进来而不会对应用逻辑造成任何 影响。造成Client阻塞,性能降低以及无法进行额外的控制的原因是由于所有的服务调用都是同步的,解决方案很简单,改为异步调用方式,把服务的调用和 服务的执行分离。
首先来介绍一个概念,活动对象(Active Object)。所谓活动对象是相对于被动对象(passive object)而言的,被动对象的方法的调用和执行都是在同一个线程中的,被动对象方法的调用是同步的、阻塞的,一般的对象都属于被动对象;主动对象的方 法的调用和执行是分离的,主动对象有自己独立的执行线程,主动对象的方法的调用是由其他线程发起的,但是方法是在自己的线程中执行的,主动对象方法的调用 是异步的,非阻塞的。
本框架的核心就是使用主动对象来封装并发逻辑,然后把Client的请求转发给实际的服务 提供者(应用逻辑),这样无论是Client还是实际的服务提供者都不用关心并发的存在,不用考虑并发所带来的数据一致性问题。从而实现应用逻辑和并发逻 辑的隔离,服务调用和服务执行的隔离。下面给出关键的实现细节。
本框架有如下几部分构成:
一个ActiveObject类,从Thread继承,封装了并发逻辑的活动对象
一个ActiveQueue类,主要用来存放调用者请求
一个MethodRequest接口,主要用来封装调用者的请求,Command设计模式的一种实现方式
它们的一个简单的实现如下:
Java代码
//MethodRequest接口定义 
        interface MethodRequest 

    public void call(); 

//ActiveQueue定义,其实就是一个producer/consumer队列 
    class ActiveQueue 

            public ActiveQueue() { 
        _queue = new Stack(); 
            } 
    public synchronized void enqueue(MethodRequest mr) { 
        while(_queue.size() > QUEUE_SIZE) { 
            try { 
                   wait(); 
            }catch (InterruptedException e) { 
                   e.printStackTrace(); 
            }    
        } 
          
        _queue.push(mr); 
        notifyAll(); 
        System.out.println("Leave Queue"); 
    } 
    public synchronized MethodRequest dequeue() { 
        MethodRequest mr; 
         
        while(_queue.empty()) { 
            try { 
                wait(); 
            }catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
        mr = (MethodRequest)_queue.pop(); 
        notifyAll(); 
         
    return mr; 
    }     
    private Stack _queue; 
    private final static int QUEUE_SIZE = 20; 

//ActiveObject的定义 
class ActiveObject extends Thread 

    public ActiveObject() { 
        _queue = new ActiveQueue(); 
        start(); 
    } 
    public void enqueue(MethodRequest mr) { 
        _queue.enqueue(mr); 
    } 
    public void run() { 
        while(true) { 
            MethodRequest mr = _queue.dequeue(); 
            mr.call(); 
        } 
    }  
    private ActiveQueue _queue; 



通过上面的代码可以看出正是这些类相互合作完成了对并发逻辑的封装。开发者只需要根据需要实现MethodRequest接口,另外再定义一个服务代理类提供给使用者,在服务代理者类中把服务调用者的请求转化为MethodRequest实现,交给活动对象即可。
使用该框架,可以较好的做到应用逻辑和并发模型的分离,从而使开发者集中精力于应用领域,然后平滑的和并发模型结合起来,并且可以针对ActiveQueue定制排队机制,比如基于优先级等。



基于框架的解决方案

本小节将使用上述的框架重新实现前面的例子,提供对于并发的支持。第一步先完成对于MethodRequest的实现,对于我们的例子来说实现如下:
Java代码
class SayHello implements MethodRequest 

    public SayHello(Service s) { 
        _service = s; 
    } 
    public void call() { 
        _service.sayHello(); 
    } 
    private Service _service; 



该类完成了对于服务提供接口sayHello方法的封装。接下来定义一个服务代理类,来完成请求的封装、排队功能,当然为了做到对Client透明,该类必须实现Service接口。定义如下:
Java代码
class ServiceProxy implements Service 

    public ServiceProxy() { 
        _service = new ServiceImp(); 
        _active_object = new ActiveObject(); 
    } 
     
    public void sayHello() { 
        MethodRequest mr = new SayHello(_service); 
        _active_object.enqueue(mr); 
    } 
    private Service _service; 
    private ActiveObject _active_object; 



其他的类和接口定义不变,下面对比一下并发逻辑增加前后的服务调用的变化,并发逻辑增加前,对于sayHello服务的调用方法:
Java代码
Service s = new ServiceImp(); 
    Client c = new Client(s); 
    c.requestService(); 

并发逻辑增加后,对于sayHello服务的调用方法:
Java代码
Service s = new  ServiceProxy(); 
Client c = new Client(s); 
c.requestService(); 


可以看出并发逻辑增加前后对于Client的ServiceImp都无需作任何改变,使用方式也非常一致,ServiceImp也能够独立的进行重用。类结构图如下:


读者容易看出,使用框架也增加了一些复杂性,对于一些简单的应用来说可能根本就没有必要使用本框架。希望读者能够根据自己的实际情况进行判断。



结论

本 文围绕一个简单的例子论述了如何构架一个Java并发模型框架,其中使用了一些构建框架的常用技术,当然所构建的框架和一些成熟的商用框架相比,显得非常 稚嫩,比如没有考虑服务调用有返回值的情况,但是其思想方法是一致的,希望读者能够深加领会,这样无论对于构建自己的框架还是理解一些其他的框架都是很有 帮助的。读者可以对本文中的框架进行扩充,直接应用到自己的工作中。参考文献〔1〕中对于构建并发模型框架中的很多细节问题进行了深入的论述,有兴趣的读 者可以自行研究。下面列出本框架的优缺点:
优点:
增强了应用的并发性,简化了同步控制的复杂性
服务的请求和服务的执行分离,使得可以对服务请求排队,进行灵活的控制
应用逻辑和并发模型分离,使得程序结构清晰,易于维护、重用
可以使开发者集中精力于应用领域
缺点:
由于框架所需类的存在,在一定程度上增加了程序的复杂性
如果应用需要过多的活动对象,由于线程切换开销会造成性能下降
可能会造成调试困难
分享到:
评论

相关推荐

    Scratch图形化编程语言入门与进阶指南

    内容概要:本文全面介绍了Scratch编程语言,包括其历史、发展、特点、主要组件以及如何进行基本和进阶编程操作。通过具体示例,展示了如何利用代码块制作动画、游戏和音乐艺术作品,并介绍了物理模拟、网络编程和扩展库等功能。 适合人群:编程初学者、教育工作者、青少年学生及对编程感兴趣的各年龄段用户。 使用场景及目标:①帮助初学者理解编程的基本概念和逻辑;②提高学生的创造力、逻辑思维能力和问题解决能力;③引导用户通过实践掌握Scratch的基本和高级功能,制作个性化作品。 其他说明:除了基础教学,文章还提供了丰富的学习资源和社区支持,帮助用户进一步提升技能。

    mmexport1734874094130.jpg

    mmexport1734874094130.jpg

    基于simulink的悬架仿真模型,有主动悬架被动悬架天棚控制半主动悬架 1基于pid控制的四自由度主被动悬架仿真模型 2基于模糊控制的二自由度仿真模型,对比pid控制对比被动控制,的比较说明

    基于simulink的悬架仿真模型,有主动悬架被动悬架天棚控制半主动悬架 [1]基于pid控制的四自由度主被动悬架仿真模型 [2]基于模糊控制的二自由度仿真模型,对比pid控制对比被动控制,的比较说明 [3]基于天棚控制的二自由度悬架仿真 以上模型,说明文档齐全,仿真效果明显

    【组合数学答案】组合数学-苏大李凡长版-课后习题答案

    内容概要:本文档是《组合数学答案-网络流传版.pdf》的内容,主要包含了排列组合的基础知识以及一些经典的组合数学题目。这些题目涵盖了从排列数计算、二项式定理的应用到容斥原理的实际应用等方面。通过对这些题目的解析,帮助读者加深对组合数学概念和技巧的理解。 适用人群:适合初学者和有一定基础的学习者。 使用场景及目标:可以在学习组合数学课程时作为练习题参考,也可以在复习考试或准备竞赛时使用,目的是提高解决组合数学问题的能力。 其他说明:文档中的题目覆盖了组合数学的基本知识点,适合逐步深入学习。每个题目都有详细的解答步骤,有助于读者掌握解题思路和方法。

    YOLO算法-雨水排放涵洞模型数据集-1000张图像带标签-.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    操作系统实验 Ucore lab5

    操作系统实验 Ucore lab5

    学生成绩管理系统软件界面

    基于matlab开发的学生成绩管理系统GUI界面,可以实现学生成绩载入,显示,处理及查询。

    NVR-K51-BL-CN-V4.50.010-210322

    老版本4.0固件,(.dav固件包),支持7700N-K4,7900N-K4等K51平台,升级后出现异常或变砖可使用此版本。请核对自己的机器信息,确认适用后在下载。

    YOLO算法-塑料数据集-7张图像带标签-塑料.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    YOLO算法-杂草检测项目数据集-3970张图像带标签-杂草.zip

    YOLO算法-杂草检测项目数据集-3970张图像带标签-杂草.zip

    E008 库洛米(3页).zip

    E008 库洛米(3页).zip

    基于西门子 PLC 的晶圆研磨机自动控制系统设计与实现-论文

    内容概要:本文详细阐述了基于西门子PLC的晶圆研磨机自动控制系统的设计与实现。该系统结合了传感器技术、电机驱动技术和人机界面技术,实现了晶圆研磨过程的高精度和高效率控制。文中详细介绍了控制系统的硬件选型与设计、软件编程与功能实现,通过实验测试和实际应用案例验证了系统的稳定性和可靠性。 适合人群:具备一定的自动化控制和机械设计基础的工程师、研究人员以及从事半导体制造的技术人员。 使用场景及目标:本研究为半导体制造企业提供了一种有效的自动化解决方案,旨在提高晶圆研磨的质量和生产效率,降低劳动强度和生产成本。系统适用于不同规格晶圆的研磨作业,可以实现高精度、高效率、自动化的晶圆研磨过程。 阅读建议:阅读本文时,重点关注晶圆研磨工艺流程和技术要求,控制系统的硬件和软件设计方法,以及实验测试和结果分析。这将有助于读者理解和掌握该自动控制系统的实现原理和应用价值。

    YOLO算法-禾本科杂草数据集-4760张图像带标签.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    深圳建筑安装公司“挖掘机安全操作规程”.docx

    深圳建筑安装公司“挖掘机安全操作规程”

    YOLO算法-汽车数据集-120张图像带标签-汽车.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    大题解题方法等4个文件.zip

    大题解题方法等4个文件.zip

    保障性安居工程考评内容和评价标准.docx

    保障性安居工程考评内容和评价标准.docx

    监督机构检查记录表.docx

    监督机构检查记录表.docx

    (177588850)基于java+mysql+swing的学生选课成绩信息系统

    该项目适合初学者进行学习,有效的掌握java、swing、mysql等技术的基础知识。资源包含源码、视频和文档 资源下载|如果你正在做毕业设计,需要源码和论文,各类课题都可以,私聊我。 商务合作|如果你是在校大学生,正好你又懂语言编程,或者你可以找来需要做毕设的伙伴,私聊我。。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    218) Leverage - 创意机构与作品集 WordPress 主题 2.2.7.zip

    218) Leverage - 创意机构与作品集 WordPress 主题 2.2.7.zip

Global site tag (gtag.js) - Google Analytics