精华内容
下载资源
问答
  • 新书推荐《并发模式与应用实践》点击上图了解及购买使用多线程、同步和函数式编程的模式构建可扩展的应用程序,提供大量代码示例。编辑推荐选择正确的并发(concurrent)体系结构对应用程序...

    新书推荐

    并发模式与应用实践

    点击上图了解及购买

    使用多线程、同步和函数式编程的模式构建可扩展的应用程序,提供大量代码示例。

    编辑推荐

    选择正确的并发(concurrent)体系结构对应用程序的设计和性能有重大影响,这本书解释了如何利用并行(parallel)体系结构的不同特性,使代码更快、更高效地运行。

    本书全面阐述并发设计模式,并展示这些模式背后的基本原理及设计方案。书中首先介绍基本的并发概念,并探索显式锁定、无锁编程、future模式和actor模式;然后深入探讨不同的并发模型和并行算法,并将它们应用到不同的实际场景中,以挖掘应用程序的真正潜力。本书将帮助你完整了解多线程设计模式,如主/从模式、leader/followers模式、map-reduce模式以及监视器模式,还将帮助你使用这些模式进行实际编码。

    通过阅读本书,你将:

    • 探索并行体系结构。

    • 熟悉并发模型。

    • 通过实现多线程模式来内化设计主题。

    • 深入了解并发设计模式。

    • 发现众多Java线程抽象背后的设计原则。

    • 使用函数式并发模式。

    内容简介

    本书解释了如何利用并行体系结构的不同特性,使代码更快、更高效。首先介绍基本的并发概念,并探索围绕显式锁定、无锁编程、future模式和actor模式。其次,深入讲解不同的并发模型和并行算法,并将它们应用到不同的场景中,以挖掘应用程序的真正潜力。本书将带读者了解多线程设计模式,如主/从模式,Leader/Followers模式,map-reduce模式,以及监视器模式,还将帮助读者学习使用这些模式的实际编码。

    作者简介

    阿图尔·S. 科德(Atul S. Khot)用C和C++编写过软件程序,作为一名Linux迷和擅长使用命令行的程序员,他精通多种编程语言。在大量使用Java编程并涉猎多种语言之后,现在他越来越喜欢Scala、Clojure和Erlang。他经常作为演讲嘉宾参加软件会议,曾担任Dobb博士产品奖评委。他是《Scala Functional Programming Patterns》和《Learning Functional Data Structures and Algorithms》的作者。

    目录

    译者序

    前言

    作者/评阅者简介

    第1章 并发简介1

    1.1 并发轻而易举2

    1.1.1 推动并发3

    1.1.2 分时6

    1.1.3 两种并发编程模型7

    1.2 消息传递模型8

    1.2.1 协调和通信10

    1.2.2 流控制12

    1.2.3 分治策略14

    1.2.4 进程状态的概念15

    1.3 共享内存和共享状态模型16

    1.3.1 线程交错—同步的需要18

    1.3.2 竞争条件和海森堡bug20

    1.3.3 正确的内存可见性和happens-before原则21

    1.3.4 共享、阻塞和公平22

    1.3.5 异步与同步执行24

    1.3.6 Java的非阻塞I/O25

    1.4 模式和范式26

    1.4.1 事件驱动的架构28

    1.4.2 响应式编程29

    1.4.3 actor范式31

    1.4.4 消息代理32

    1.4.5 软件事务性内存33

    1.4.6 并行集合34

    1.5 本章小结35

    第2章 并发模式初探37

    2.1 线程及其上下文38

    2.2 竞争条件40

    2.2.1 监视器模式44

    2.2.2 线程安全性、正确性和不变性45

    2.2.3 双重检查锁定48

    2.2.4 显式锁定52

    2.2.5 生产者/消费者模式60

    2.2.6 比较和交换66

    2.3 本章小结68

    第3章 更多的线程模式70

    3.1 有界缓冲区72

    3.1.1 策略模式—客户端轮询74

    3.1.2 接管轮询和睡眠的策略75

    3.1.3 使用条件变量的策略77

    3.2 读写锁78

    3.2.1 易读的RW锁80

    3.2.2 公平锁84

    3.3 计数信号量86

    3.4 我们自己的重入锁89

    3.5 倒计时锁存器91

    3.6 循环屏障95

    3.7 future任务97

    3.8 本章小结100

    第4章 线程池101

    4.1 线程池102

    4.1.1 命令设计模式104

    4.1.2 单词统计105

    4.1.3 单词统计的另一个版本107

    4.1.4 阻塞队列107

    4.1.5 线程中断语义111

    4.2 fork-join池111

    4.2.1 Egrep—简易版112

    4.2.2 为什么要使用递归任务113

    4.2.3 任务并行性116

    4.2.4 使用fork-join API实现快速排序117

    4.2.5 map-reduce技术124

    4.3 线程的工作窃取算法125

    4.4 主动对象128

    4.4.1 隐藏和适应129

    4.4.2 使用代理129

    4.5 本章小结132

    第5章 提升并发性133

    5.1 无锁堆栈134

    5.1.1 原子引用134

    5.1.2 堆栈的实现135

    5.2 无锁的FIFO队列137

    5.2.1 流程如何运作140

    5.2.2 无锁队列141

    5.2.3 ABA问题147

    5.3 并发的哈希算法152

    5.3.1 add(v)方法153

    5.3.2 contains(v)方法156

    5.4 大锁的方法157

    5.5 锁条纹设计模式159

    5.6 本章小结162

    第6章 函数式并发模式163

    6.1 不变性164

    6.1.1 不可修改的包装器165

    6.1.2 持久数据结构167

    6.1.3 递归和不变性169

    6.2 future模式170

    6.2.1 apply方法171

    6.2.2 future—线程映射173

    6.2.3 future模式是异步的174

    6.2.4 糟糕的阻塞177

    6.2.5 函数组合179

    6.3 本章小结182

    第7章 actor模式183

    7.1 消息驱动的并发183

    7.1.1 什么是actor185

    7.1.2 状态封装189

    7.1.3 并行性在哪里190

    7.1.4 未处理的消息192

    7.1.5 become模式193

    7.1.6 让它崩溃并恢复197

    7.1.7 actor通信—ask模式199

    7.1.8 actor通信—tell模式204

    7.1.9 pipeTo模式205

    7.2 本章小结207

    你与世界

    只差一个

    公众号

    展开全文
  • 简介:  Active Object 是并发编程实践中典型的设计模式,Active Object ...Active Object 并发模式在 Java 中的应用。   本文主要从以下两个方面进行阐述: 使用 C++ 语言,来描述 Active Obj...

    简介:  Active Object 是并发编程实践中典型的设计模式,Active Object 模式的核心是通过解耦合方法的调用与执行来提高程序的并发度。本文将从典型 Active Object 设计模式入手,从一个新的视角来探讨 Active Object 并发模式在 Java 中的应用。

     

    本文主要从以下两个方面进行阐述:

    • 使用 C++ 语言,来描述 Active Object 设计模式。

      Java 类库对于这样一个典型的模式做了很好的类库层面的封装,因此对于 Java 的开发者来说,很多关于该设计模式本身的东西被屏蔽掉了。本文试图使用 Native C++ 语言,帮助读者从本质上对 Active Object 设计模式有一个更全面的认识。

    • 结合 C++ 版本的 Active Object 设计模式,引领读者对于 Active Object 设计模式在 Java 类库中的支持,有一个更深刻的认识,帮助读者正确并且有效地使用这些类库。

    预备知识

    并发对象 (Concurrent Object)

    在这里,我们先定义一下,什么是并发对象。不同于一般的对象,并发对象指的是该对象方法的调用与方法的执行不在同一个线程內,也即:该对象方 法被异步执行。这其实是在多线程编程环境下典型的计算特征,线程引入了异步。从另一个角度来看,并发对象其实是面向对象的概念应用于多线程计算环境下的产 物。

     

    Active Object 设计模式 C++ 描述

    我们将从以下几个方面来讨论 Active Object 模式。

    问题描述

    我们都知道,基于多线程机制的并发编程模式可以极大提高应用的 QoS(Quality of Service)。典型的例子如,我们在开发服务器端的应用时,通行的做法就是通过多线程机制并发地服务客户端提交上来的请求,以期提高服务器对客户端的 反应度 (Responsiveness)。同时,相比于单线程的应用,并发的多线程应用相对要复杂得多。在多线程的计算环境里,并发对象被所有的调用者线程所共 享。一般来说,并发对象的设计实现需要考虑下面的几个重要因素:

    • 并发对象的任何一次的方法执行,不允许无限地或者长时间阻止其它方法的调用执行,从而影响应用的 QoS。
    • 由于并发对象被调用者线程所共享,其内部状态必须保证是线程安全的,必须受限于某些线程同步约束,并且这些约束对调用者来说是透明的,不可见的。从调用者的角度来看,并发对象与普通对象没有多大区别。
    • 并发对象的设计实现,应该能够透明地利用底层平台所提供的并发机制。这样做的好处是,当并发对象运行在诸如多核处理器这类底层硬件平台上时,我们的应用能够充分挖掘底层平台带来的并发优势,以获得足够好的应用性能。

    我们使用 Active Object 设计模式来解决这些问题。

    Active Object 设计模式的本质是解耦合方法的调用 (Method invocation) 与方法的执行 (Method execution),方法调用发生在调用者线程上下文中,而方法的执行发生在独立于调用者线程的 Active Object 线程上下文中。并且重要的一点是,该方法与其它普通的对象成员方法对于调用者来说,没有什么特别的不同。从运行时的角度来看,这里涉及到两类线程,一个是 调用者线程,另外一个是 Active Object 线程,我们会在下面更详细地谈到。

    结构

    在 Active Object 模式中,主要有以下几种类型的参与者:

    • 代理 (Proxy) :代理是 Active Object 所定义的对于调用者的公共接口。运行时,代理运行在调用者线程的上下文中,负责把调用者的方法调用转换成相应的方法请求 (Method Request),并将其插入相应的 Activation List,最后返回给调用者 Future 对象。
    • 方法请求:方法请求定义了方法执行所需要的上下文信息,诸如调用参数等。
    • Activation List:负责存储所有由代理创建的,等待执行的方法请求。从运行时来看,Activation List 会被包括调用者线程及其 Active Object 线程并发存取访问,所以,Activation List 实现应当是线程安全的。
    • 调度者 (Scheduler):调度者运行在 Active Object 线程中,调度者来决定下一个执行的方法请求,而调度策略可以基于很多种标准,比如根据方法请求被插入的顺序 FIFO 或者 LIFO,比如根据方法请求的优先级等等。
    • Servant: Servant 定义了 Active Object 的行为和状态,它是 Proxy 所定义的接口的事实实现。
    • Future: 调用者调用 Proxy 所定义的方法,获得 Future 对象。调用者可以从该 Future 对象获得方法执行的最终结果。在真实的实现里,Future 对象会保留一个私有的空间,用来存放 Servant 方法执行的结果。

    执行序列图

    在 Active Object 设计模式中,在参与者之间将发生如下的协作过程:

    1. 方法请求的构造与调度。调用者调用 Proxy 的方法 method(),Proxy 创建相应的方法请求,把它传给调度者 (Scheduler),调度者负责把该方法请求放入 Activation List 中。如果 method() 需要返回执行结果,Proxy 返回一个 Future 对象给调用者(图 1 中步骤 1 到步骤 6)。
    2. 方法请求执行。调度者负责从 Activation List 队列里按照预先定义的规则拿出下一个可执行的方法请求,并把该请求绑定到相应 Servant 所定义的方法(图 1 中步骤 7 到步骤 11)。
    3. 完成阶段。保存任何 Servant 方法执行的结果到 Future 对象中去(图 1 中步骤 12)。重复第二步,调度者继续轮询 Activation List 队列,看是否有下一个可执行的方法请求。


    图 1. Active Object Sequence Diagram.
    图 1. Active Object Sequence Diagram.

    从图 1 我们可以看到,步骤 1 到步骤 6 运行在调用者线程中,而步骤 7 到步骤 12 运行在 Active Object 的线程中。

    实现

    在本节中,我们给出 Active Object 的 C++ 示例实现。

    调用者调用 Proxy 的 get() 方法,从 Active Object 获得 Message。我们可以假定,在真实的应用中, get() 方法的实现受制于某些慢速的 IO 操作,比如需要通过 TCP Socket 从远端的机器获得 Message, 然后返回给调用者。所以我们使用 Active Object 来实现该应用,通过线程的并发达到提高应用的 QoS。

    1. 实现 Servant,如清单 1 所示:

      清单 1. MQ_Servant
      						
      class MQ_Servant {
      public:
          // Constructor and destructor.
          MQ_Servant (size_t mq_size);
          virtual ~MQ_Servant ();
      
          // Message queue implementation operations.
          void put (const Message &msg);
          Message get ();
      
           // Predicates.
          bool empty () const;
          bool full () const;
      private:
          // Internal queue representation, e.g., a circular
          // array or a linked list, that does not use any
          // internal synchronization mechanism.
      };
      

      MQ_Servant 是真正的服务提供者,实现了 Proxy 中定义的方法。put() 和 get() 方法用来操作底层的队列。另外,Servant 的实现是纯粹的应用逻辑实现,或者称为商业逻辑实现,没有混合任何的线程同步机制 , 这有利于我们进行应用逻辑的重用,而不需要考虑不同的线程同步机制。

    2. 实现 Proxy,如清单 2 所示:

      清单 2. MQ_Proxy
      						
      class MQ_Proxy {
      public:
          // Bound the message queue size.
          enum { MQ_MAX_SIZE = 100 };
          MQ_Proxy (size_t size = MQ_MAX_SIZE)
          :scheduler_ (size), 
          servant_ (size) { 
          }
      
          // Schedule <put> to execute on the active object.
          void put (const Message &msg) {
              Method_Request *mr = new Put(servant_,msg);
              scheduler_.insert (mr);
          }
      
          // Return a <Message_Future> as the "future" result of
          // an asynchronous <get> method on the active object.
          Message_Future get () {
              Message_Future result;
              Method_Request *mr = new Get (&servant_,result);
              scheduler_.insert (mr);
              return result;
          }
      
          // empty() and full() predicate implementations ...
      private:
          // The servant that implements the active object
          // methods and a scheduler for the message queue.
          MQ_Servant servant_;
          MQ_Scheduler scheduler_;
      };
      

      同一个进程中的多个调用者线程可以共享同一个 Proxy。

    3. 实现 Method Request,如清单 3 所示:

      清单 3. Method_Request
      						
      class Method_Request {
      public:
          // Evaluate the synchronization constraint.
          virtual bool can_run () const = 0
          // Execute the method. 
          virtual void call () = 0;
      };
      // Inherites from Method_Request
      class Get : public Method_Request {
      public:
          Get (MQ_Servant *rep, const Message_Future &f)
          :servant_ (rep), 
          result_ (f) 
          { 
          }
          virtual bool can_run () const {
              // Synchronization constraint: cannot call a 
              // <get> method until queue is not empty.
              return !servant_->empty ();
          }
      
          virtual void call () {
              // Bind dequeued message to the future result.
              result_ = servant_->get ();
          }
      private:
          MQ_Servant *servant_;
          Message_Future result_;
      };
      

    4. 实现 Activation List,如清单 4 所示:

      清单 4. Activation_List
      						
      class Activation_List {
      public:
          // Block for an "infinite" amount of time waiting
          // for <insert> and <remove> methods to complete.
          enum { INFINITE = -1 };
      
          // Define a "trait".
          typedef Activation_List_Iterator iterator;
      
          Activation_List ();
      
          // Insert <method_request> into the list, waiting up
          // to <timeout> amount of time for space to become
          // available in the queue. Throws the <System_Ex>
          // exception if <timeout> expires.
          void insert (Method_Request *method_request,Time_Value *timeout = 0);
      
          // Remove <method_request> from the list, waiting up
          // to <timeout> amount of time for a <method_request>
          // to be inserted into the list. Throws the
          // <System_Ex> exception if <timeout> expires.
          void remove (Method_Request *&method_request, Time_Value *timeout = 0);
      
      private:
          // Synchronization mechanisms, e.g., condition
          // variables and mutexes, and the queue implementation, 
               // e.g., an array or a linked list, go here.
      };
      

      Activation List 的实际上就是一个线程同步机制保护下的 Method Request 队列,对该队列的所有操作 (insert/remove) 都应该是线程安全的。从本质上讲,Activation List 所基于的就是典型的生产者 / 消费者并发编程模型,调用者线程作为生产者把 Method Request 放入该队列,Active Object 线程作为消费者从该队列拿出 Method Request, 并执行。

    5. 实现 Scheduler,如清单 5 所示:

      清单 5. MQ_Scheduler
      						
      class MQ_Scheduler {
      public:
          // Initialize the <Activation_List> and make <MQ_Scheduler>
          // run in its own thread of control.
               // we call this thread as Active Object thread.
          MQ_Scheduler ()
          : act_list_() {
              // Spawn separate thread to dispatch method requests.
              // The following call is leveraging the parallelism available on native OS
              // transparently
              Thread_Manager::instance ()->spawn (&svc_run,this);
          }
          // ... Other constructors/destructors, etc.
      
          // Put <Method_Request> into <Activation_List>. This
          // method runs in the thread of its client,i.e.
          // in the proxy's thread.
          void insert (Method_Request *mr) {
              act_list_.insert (mr);
          }
      
          // Dispatch the method requests on their servant
          // in its scheduler's thread of control.
          virtual void dispatch () {
              // Iterate continuously in a separate thread(Active Object thread).
              for (;;) {
                  Activation_List::iterator request;
                  // The iterator's <begin> method blocks
                  // when the <Activation_List> is empty.
                  for(request = act_list_.begin (); request != act_list_.end ();++request){
                      // Select a method request whose
                      // guard evaluates to true.
                      if ((*request).can_run ()) {
                          // Take <request> off the list.
                          act_list_.remove (*request);
                          (*request).call () ;
                          delete *request;
                      }
      
                      // Other scheduling activities can go here,
                      // e.g., to handle when no <Method_Request>s
                      // in the <Activation_List> have <can_run>
                      // methods that evaluate to true.
      
                  }
      
              }
          }
      
      private:
          // List of pending Method_Requests.
          Activation_List act_list_;
      
          // Entry point into the new thread.
          static void *svc_run (void *arg) {
              MQ_Scheduler *this_obj =    static_cast<MQ_Scheduler *> (args);
              this_obj->dispatch ();
          }
      };
      

    6. 实现 Future,如清单 6 所示:

      清单 6. Message_Future
      						
      class Message_Future {
      public:
          // Initializes <Message_Future> to
          // point to <message> immediately.
          Message_Future (const Message &message);
      
          //Other implementatio……
      
          // Block upto <timeout> time waiting to obtain result
          // of an asynchronous method invocation. Throws
          // <System_Ex> exception if <timeout> expires.
          Message result (Time_Value *timeout = 0) const;
      private:
          //members definition here……
      };
      

      事实上,对于调用者来说,可以通过以下的方式从 Future 对象获得真实的执行结果 Message:

      • 同步等待。调用者调用 Future 对象的 result() 方法同步等待,直到后端的 Servant 相应方法执行结束,并把结果存储到了 Future 对象中来,result 返回,调用者获得 Message。
      • 同步超时等待。调用者调用 Future 对象的 result(timeout) 方法。如果过了 timeout 时间之后,后端的 Servant 相应方法执行仍未结束,则调用失败,否则,调用者线程被唤醒,result 方法返回,调用者获得 Message。
      • 异步查询。调用者可以通过调用 Future 对象定义的查询方法 ( 清单 6 没有提供相应的定义 ),查看真实的结果是否准备好了,如果准备好了,调用 result 方法,直接获得 Message。

      清单 7 是使用该 Active Object 的示例。



      清单 7. Active Object 使用
      						
      MQ_Proxy message_queue;
      
      //Optioin 1. Obtain future and block thread until message arrives.
      Message_Future future = message_queue.get();
      Message msg = future.result();
      //Handle received message here
      handle(msg);
      
      //2. Obtain a future (does not block the client).
      Message_Future future = message_queue.get ();
      
      //The current thread is not blocked, do something else here...
      //Evaluate future and block if result is not available.
      Message msg = future.result ();
      //Handle received message here
      handle(msg);
      

      从清单 7 可以看到,MQ_Proxy 对于调用者而言,和一个普通的 C++ 定义的对象并没有区别,并发的实现细节已经被隐藏。

    Java 对 Active Object 支持

    Java JDK 1.3 引入了 java.util.Timer 和 java.util.TimerTask,提供了对 timer-based 并发任务支持,Timer 和 TimerTask 可以看作是 Active Object 设计模式在 Java 中的实现。不过,在这里我们不打算过多讨论 Timer 及其 TimerTask。由于 Timer 和 TimerTask 的缺陷性,例如 Timer 使用单线程执行 TimerTask 导致的 Task 调度时间的不精确性等问题。从 Java1.5 开始,Java 建议使用 ScheduledThreadPoolExecutor 作为 Timer 的替代。

    在这里,我们讨论一下自 Java1.5 引入的 Executor Framework。Java1.5 的 Executor Framework 可以看作是 Active Object 设计模式在 Java 中的体现。不过 Java 的 Executor Framework 极大地简化了我们前面所讨论的 Active Object 所定义的模式。

    Java 的 Executor Framework 是一套灵活强大的异步任务执行框架,它提供了标准的方式解耦合任务的提交与任务的执行。Java Executor 框架中的任务指的是实现了 Runnable 或者 Callable 接口的对象。Executor 的示例用法如清单 8 所示:


    清单 8. Java Executor 示例代码

    				
    public class TaskExecutionTcpServer {
        private static final int NTHREADS = 100;
        private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
    
        public static void main(String[] args) throws IOException {
            ServerSocket socket = new ServerSocket(80);
            while (true) {
                final Socket connection = socket.accept();
                Runnable task = new Runnable() {
                    public void run() {
                        handleRequest(connection);
                    }
                    public void handleRequest(Socket connection) {
                        // Handle the incoming socket connection from                         
                        //individual client.
                    }
                };
                exec.execute(task);
            }
        }
    }
    

     

    在示例 8 中,我们创建了一个基于线程池的 Java Executor, 每当新的 TCP 连接进来的时候,我们就分配一个独立的实现了 Runnable 任务来处理该连接,所有这些任务运行在我们创建的有 100 个线程的线程池上。

    我们可以从 Active Object 设计模式的角度来审视一下 Java Executor 框架。Java Executor 框架以任务 (Task) 为中心,简化了 Active Object 中的角色分工。可以看到,实现 Runnable 或者 Callable 接口的 Java Executor 任务整合了 Method Request 和 Servant 的角色 , 通过实现 run() 或者 call() 方法实现应用逻辑。Java Executor 框架并没有显式地定义 Proxy 接口,而是直接调用 Executor 提交任务,这里的 Executor 相当于 Active Object 中调度者角色。从调用者的角度来看,这看起来并不像是在调用一个普通对象方法,而是向 Executor 提交了一个任务。所以,在这个层面上说,并发的底层细节已经暴露给了调用者。对于 Java 的开发者来说,如果你不担心这样的底层并发细节直接暴露给调用者,或者说你的应用并不需要像对待普通对象一样对待并发对象,Java 的 Executor 框架是一个很好的选择。相反,如果你希望隐藏这样的并发细节,希望像操纵普通对象一样操纵并发对象,那你就需要如本文上节所描述的那样,遵循 Active Object 设计原则 , 清晰地定义各个角色,实现自己的 Active Object 模式。

    总而言之,Java Executor 框架简化了 Active Object 所定义的模式,模糊了 Active Object 中角色的分工,其基于生产者 / 消费者模式,生产者和消费者基于任务相互协作。

     

    总结

    最后,我们讨论一下 Active Object 设计模式的优缺点。

    Active Object 给我们的应用带来的好处:

    • 极大提高了应用的并发性以及简化了线程同步带来的复杂性。并发性的提高得益于调用者线程与 Active Object 线程的并发执行。简化的线程同步复杂性主要表现在所有线程同步细节封装在调度者内 ( 也就是 Java 的 Executor 对象 ),Active Object 调用者并不需要关心。
    • 在 Active Object 中,方法的执行顺序可以不同于方法的调用顺序。用 Java 的话说,也就是任务执行的顺序可以不同于任务提交的顺序。在一定情况下,这可以帮助优化我们应用的性能,提高应用的 QoS 及其 Responsiveness。在 Java Executor 框架下,你可以根据当前的计算资源,确定优化的执行策略 (Execution Policy),该执行策略的内容包括:任务将分配在多少线程上执行,以什么顺序执行,多少任务可以同时执行等等。

    当然,Active Object 也有缺点:

    • 额外的性能开销。这涉及到从调用者线程到 Active Object 线程的上下文切换,线程同步,额外的内存拷贝等。
    • 难于调试。Active Object 引入了方法的异步执行,从调试者的角度看,调试这样的方法调用不像普通方法那样直截了当,并且这其中涉及到了线程的调度,同步等。

     

    参考资料

    学习

    • JDK 5.0 中的并发 ”(developerWorks,2004 年 12 月):本教程将介绍 JDK 5.0 提供的用于并发的新实用程序类,并通过与现有并发原语(synchronized、wait() 和 notify())相比较,说明这些类如何提高了可伸缩性。

    • Java 多线程与并发编程专题 ” (developerWorks,2008 年 6 月):Java 平台提供了一套广泛而功能强大的 API、工具和技术。其中,内建支持线程是它的一个强大的功能。这一功能为使用 Java 编程语言的程序员提供了并发编程这一诱人但同时也非常具有挑战性的选择。本专题汇集了与 Java 多线程与并发编程相关的文章和教程,帮助读者理解 Java 并发编程的模式及其利弊,向读者展示了如何更精确地使用 Java 平台的线程模型。

    • Java 设计模式与建模专题 ”(developerWorks,2008 年 1 月):本专题为 Java 软件工程师们提供了面向 Java 的设计模式和建模方面相关的文章和教程。帮助读者理解、学习作为专业软件工程师必需掌握的设计模式与建模技术。

    • 技术书店 :浏览关于这些和其他技术主题的图书。

    • developerWorks Java 技术专区 :数百篇关于 Java 编程各个方面的文章。

     

    原文:http://www.ibm.com/developerworks/cn/java/j-lo-activeobject/index.html?ca=drs-

     

     

     

     

     

     

    展开全文
  •  欢迎访问本书网页: [a href="http://www.cs.wustl.edu/%7eschmidt/posa"]http://www.cs.wustl.edu/~schmidt/posa[/a] 本书讲述用于创建并发和网络化系统的模式,共涉及17种模式与惯用法。这些模式构成了...
  • 并发编程是Java开发人员面试或者工作中必不可少的一部分,资深Java专家10年经验总结,从原理到应用,用实战来讲解,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践!分享并发...

    并发编程是Java开发人员面试或者工作中必不可少的一部分,资深Java专家10年经验总结,从原理到应用,用实战来讲解,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践!分享并发编程的学习笔记:原理篇+模式篇+应用篇。

    需要这三份的小伙伴可以帮忙一键三连后加小助手vx:bjmsb2019或者vx:1249448307即可~

    并发编程——原理篇

    并发编程——模式篇

    并发编程——应用篇

    需要这三份的小伙伴可以帮忙一键三连后加小助手vx:bjmsb2019或者vx:1249448307即可~

    展开全文
  • 由于WCF的并发是针对某个封装了服务实例的InstanceContext而言的,所以在不同的实例上下文模式下,会表现出不同的并发行为。接下来,我们从具体的实例上...在《实践重于理论》一文中,我写一个了简单的WCF应用,通过...

    由于WCF的并发是针对某个封装了服务实例的InstanceContext而言的,所以在不同的实例上下文模式下,会表现出不同的并发行为。接下来,我们从具体的实例上下文模式的角度来剖析WCF的并发,如果对WCF实例上下文模式和实例上下文提供机制不了解的话,请参阅《WCF技术剖析(卷1)》第9章。

    在《实践重于理论》一文中,我写一个了简单的WCF应用,通过这个应用我们可以很清楚了监控客户端和服务操作的执行情况下。借此,我们可以和直观地看到服务端对于并发的服务调用请求,到底采用的是并行还是串行的执行方式。接下来,我们将充分地利用这个监控程序,以实例演示加原理分析相结合的方式对不同实例上下文模式下的并发实现机制进行深度剖析。

    一、单调(PerCall)实例上下文模式

    由于WCF的并发是针对某个封装了服务实例的InstanceContext而言的,但是对单调的实例上下文模式,WCF服务端运行时总是创建一个全新的InstanceContext来处理每一个请求,不管该请求是否来自相同的客户端。所以在单调实例上下文模式下,根本就不存在对某个InstanceContext的并发调用的情况发生。

    我们可以通过我们监控程序来验证这一点。为此,我们需要通过ServiceBehaviorAttribute将实例上下文模式设置成InstanceContextMode.PerCall,相关的代码如下所示。

       1: [ServiceBehavior(UseSynchronizationContext = false,InstanceContextMode = InstanceContextMode.PerCall)]
       2: public class CalculatorService : ICalculator
       3: {
       4:     //省略成员 
       5: }

    下面是客户端进行并发服务调用的代码:

       1: for (int i = 1; i <= 5; i++)
       2: {
       3:     ThreadPool.QueueUserWorkItem(state =>
       4:     {
       5:         int clientId = Interlocked.Increment(ref clientIdIndex);
       6:         ICalculator proxy = _channelFactory.CreateChannel();
       7:         using (proxy as IDisposable)
       8:         {
       9:             EventMonitor.Send(clientId, EventType.StartCall);
      10:             using (OperationContextScope contextScope = new OperationContextScope(proxy as IContextChannel))
      11:             {
      12:                 MessageHeader<int> messageHeader = new MessageHeader<int>(clientId);
      13:                 OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader.GetUntypedHeader(EventMonitor.CientIdHeaderLocalName, EventMonitor.CientIdHeaderNamespace));
      14:                 proxy.Add(1, 2);
      15:             }
      16:             EventMonitor.Send(clientId, EventType.EndCall);
      17:         }
      18:     }, null);
      19: }

    如果在此基础上运行我们的监控程序,将会得到如图1所示的输出结果,从中我们可以看出,仍然我们采用默认的并发模式(ConcurrencyMode.Single),来自5个不同客户端(服务代理)的调用请求能够及时地得到处理。

    image

    图1 单调实例上下文模式下的并发事件监控输出(不同客户端)

    上面我们演示了WCF服务端处理来自不同客户端并发请求的处理,如果5个请求来自相同的客户端,它们是否还能够及时地得到处理呢?我们不妨通过我们的监控程序来说话。现在我们需要作的是修改客户端进行服务调用的方式,让5个并发的调用来自于相同的服务代理对象,相关的代码如下所示。为了便于跟踪,我们依然将并发的序号1~5通过消息报头传递到服务端。不过在这里它不代表客户端,而是代表某个服务调用而已。

       1: ICalculator proxy = _channelFactory.CreateChannel();
       2: for (int i = 1; i < 6; i++)
       3: {
       4:     ThreadPool.QueueUserWorkItem(state =>
       5:     {
       6:         int clientId = Interlocked.Increment(ref clientIdIndex);
       7:         EventMonitor.Send(clientId, EventType.StartCall);
       8:         using (OperationContextScope contextScope = new OperationContextScope(proxy as IContextChannel))
       9:         {
      10:             MessageHeader<int> messageHeader = new MessageHeader<int>(clientId);
      11:             OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader.GetUntypedHeader(EventMonitor.CientIdHeaderLocalName, EventMonitor.CientIdHeaderNamespace));
      12:             proxy.Add(1, 2);
      13:         }
      14:         EventMonitor.Send(clientId, EventType.EndCall);
      15:     }, null);
      16: }

    再次运行我们的监控程序,你将会得到完全不一样的输出结果(如图2所示)。从监控信息我们可以很清晰地看出,服务操作的执行完全是以串行化的形式执行的。对于服务端来说,似乎仍然是以同步的方式方式处理并发的服务调用请求的。但是我们说过,WCF并发机制的同步机制是通过对InstanceContext进行加锁实现的。但是对于单调实例上下文模式来说,虽然5个请求来自相同的客户端,但是对应的InstanceContext却是不同的。难道我们前面的结论都是错误的吗?

    image

    图2 单调实例上下文模式下的并发事件监控输出(相同客户端)

    实际上出现如图2所示的监控输出与WCF并发框架体系采用的同步机制一点关系都没有。在说明原因之前,我们先来给出解决方案。我们只需要在进行服务调用之前,调用Open方法显式地开启服务代理,你就会得到与图4-5类似的输出结果,相应的代码如下所示:

       1: ICalculator proxy = _channelFactory.CreateChannel();
       2: (proxy as ICommunicationObject).Open(); 
       3: for (int i = 1; i < 6; i++)
       4: {
       5:     //省略其他代码
       6: }

    上面的问题涉及到WCF一个很隐晦的机制,相信不会有太多人知道它的存在。如果我们直接通过创建出来的服务代理对象(并没有显示开启服务代理)进行服务调用,WCF客户端框架会通过相应的机制确保服务代理的开启,我们可以将这种机制成为服务代理自动开启。在内部,WCF实际上是将本次调用放入一个队列之中,等待上一个放入队列的调用结束。也就是说,针对一个没有被显式开启的服务代理的并发调用实际上是以同步或者串行的方式执行的

    但是,如果你在进行服务调用之前通过我们上面代码的方式显式地开启服务代理,基于该代理的服务调用就能得到机制处理。所以,当你真的需要执行基于相同服务代理的并发调用的时候,请务必对服务代理进行显式开启

    并发的问题挺多,到这里还没完。现在我们保留上面修改过的代码(确保在进行并发服务调用之前显示开启服务代理),将客户端和服务终结点采用的绑定类型从WS2007HttpBinding换成NetTcpBinding或者NetNamedPipeBinding。在此运行我们的监控程序,你又将得到类似于如图2所示的监控信息。也就是说,如果采用向NetTcpBinding或者NetNamedPipeBinding这种天生就支持会话的绑定类型(因为它们基于的传输协议提供了对会话的原生支持,HTTP协议本身是没有会话的概念的),对于基于单个服务代理的同步调用,最终表现出来仍就是串行化执行。这是WCF信道架构体系设计使然,我个人对这个设计不以为然。

    二、 会话(PerSession)实例上下文模式和单例实例(Single)上下文模式

    在基于会话的实例上下文提供机制下,被创建出来封装服务实例的InstanceContext与会话(客户端或者服务代理)绑定在一起。也就是说,InstanceContext和服务代理是具有一一对应的关系。基于我们前面介绍的基于对InstanceContext加锁的同步机制,如果服务端接收到的并发调用是基于不同的客户端,那么它们会被分发给不同的InstanceContext,所以对于它们的处理是并行的。因此,我们主要探讨的是针对相同客户端的并发调用的问题。

    在《WCF技术剖析(卷1)》的第9章中,我们对WCF的会话进行过深入的剖析。如果读者对其中的内容还熟悉的话,一定知道WCF的会话最终取决于以下三个方面的因素:

    • 服务契约采用SessionMode.Allowed或者SessionMode.Required的会话模式;
    • 服务采用InstanceContextMode.PerSession的实例上下文模式;
    • 终结点的绑定提供对会话的支持。

    所以说,即使我们通过ServiceBehaviorAttribute特性将服务的实例上下文模式设置成InstanceContextMode.PerSession,如果不满足其余两个条件,WCF仍然采用的是基于单调的实例上下文提供机制,那么表现出来的并发处理行为就与单调模式别无二致了。

    我们依然可以通过我们的监控程序来证实这一点,现在我们在CalculatorService类型上应用ServiceBehaviorAttribute特性将实例上下文模式设置成InstanceContextMode.PerSession。

       1: [ServiceBehavior(UseSynchronizationContext = false,InstanceContextMode = InstanceContextMode.PerSession)]
       2: public class CalculatorService : ICalculator
       3: {
       4:     //省略成员    
       5: }

    然后我们破坏第一个条件,通过ServiceContractAttribute特性将服务契约ICalculator的会话模式设置成SessionMode.NotAllowed

       1: [ServiceContract(Namespace="http://www.artech.com/",SessionMode = SessionMode.NotAllowed)]
       2: public interface ICalculator
       3: {
       4: //省略成员
       5: }

    我们也可以破环第三个条件,让终结点绑定不支持会话。无论对WSHttpBinding还是WS2007HttpBinding,只有在支持某种安全模式或者可靠会话(Reliable Sessions)的情况下,它们才提供对会话的支持。由于WS2007HttpBinding默认采用基于消息的安全模式,如果我们将安全模式设置成None,绑定将不再支持会话。为此,我们对服务端的配置进行了如下的修改,当然客户端必须进行相应地修改,在这里就不再重复介绍了。

       1: <?xml version="1.0" encoding="utf-8" ?>
       2: <configuration>
       3:     <system.serviceModel>
       4:       <bindings>
       5:         <ws2007HttpBinding>
       6:           <binding name="nonSessionBinding">
       7:             <security mode="None"/>
       8:           </binding>
       9:         </ws2007HttpBinding>
      10:       </bindings>
      11:         <services>
      12:             <service name="Artech.ConcurrentServiceInvocation.Service.CalculatorService">
      13:                 <endpoint bindingConfiguration="nonSessionBinding" address="http://127.0.0.1:3721/calculatorservice" binding="ws2007HttpBinding" contract="Artech.ConcurrentServiceInvocation.Service.Interface.ICalculator" />
      14:             </service>
      15:         </services>
      16:     </system.serviceModel>
      17: </configuration>

    当我们进行了如此修改后再次运行我们的监控程序,你可以得到类似于如图1所示的表现为并行化处理的监控结果。

    如果同时满足上述的三个条件,来自于相同客户端的并发请求是分发到相同的InstanceContext。在这种情况下,WCF将按照相应并发模式语义上体现的行为来处理这些并发的请求。ConcurrencyMode.Single和ConcurrencyMode.Multiple体现的分别是串行化和并行化的处理方式,如果ConcurrencyMode.Reentrant,则后续的请求只有在前一个请求处理结束或者对外调用(Call Out)的时候才有机会被处理。

    对于采用单例实例上下文模式,所有的服务调用请求,不论它来自于那个客户端,最终都会被分发给同一个InstanceContext。毫无疑问,在这种情况下最终表现出来的并发处理行为与会话类似。之所以只说类似,是因为单例模式下并没有要求并发请求必须来自相同客户端的限制。


    作者:蒋金楠
    微信公众账号:大内老A
    微博:www.weibo.com/artech
    如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    展开全文
  • JAVA并发编程实践.pdf

    千次下载 热门讨论 2012-11-21 16:29:38
    《JAVA并发编程实践》随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序中取得了显著的进步,提高了Java虚拟机的性能以及并发类的可伸缩性,并加入了丰富的新并发构建块。在...
  • 由于WCF的并发是针对某个封装了服务实例的InstanceContext而言的,所以在不同的实例上下文模式下,会表现出不同... 在《实践重于理论》一文中,我写一个了简单的WCF应用,通过这个应用我们可以很清楚了监控客户端和
  • Java并发编程实践part1

    2011-04-16 21:59:40
    中文完整版的Java并发编程实践PDF电子书 作者:Brian Gogetz Tim Peierls Joshua Bloch Joseph Bowbeer David Holmes Doug Lea 译者:韩锴 方秒 目录 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 ...
  • Java并发编程实践 PDF 高清版

    千次下载 热门讨论 2010-06-25 10:44:59
    随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序取得了显著的进步,提高了Java虚拟机的性能,提高了并发类的可伸缩性,并加入了丰富的新并发构建块。在本书中,这些便利...
  • Java Concurrency in Practice Java并发编程实践 中英文版

    千次下载 热门讨论 2013-07-16 13:13:11
    Java并发编程实践,压缩包包含其中文版、英文版及书中的代码示例。喜欢本书请购买正版,英文原版及中文译本目录如下: 目录 英文原版: Listings xii Preface xvii Chapter 1: Introduction 1 1.1 A (very) brief ...
  • memcache服务应用实践

    2018-11-28 22:18:00
    1.1 Memcached常见同类软件对比 (1)Memcached是什么? Memcached是一个开源的,支持高性能,高并发的分布式内存缓存系统,由C语言编写,总共2000多行代码。从软件名称上看,前3个字符“Mem”就是内存的意思,而...
  • Netty 是一款基于 Java 的网络编程框架,能为应用程序管理复杂的网络编程、多线程处理以及并发。Netty 隐藏了样板和底层代码,让业务逻辑保持分离,更加易于复用。使用 Netty 可以得到一个易于使用的 API,让开发...
  • 第73讲:Netty底层架构系统总结与应用实践 第74讲:Netty对于异步读写操作的架构思想与观察者模式的重要应用 第75讲:适配器模式与模板方法模式在入站处理器中的应用 第76讲:Netty项目开发过程中常见且重要事项...
  • Java Series | Java 开发基础工程实践 Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的高级程序设计语言,Java 当初诞生的时候,正是上世纪 90 年代末互联网兴起的时代,在企业应用开发中存在几个问题,一...
  • Channel选择器工厂与轮询算法及注册底层实现 72_Netty线程模型深度解读与架构设计原则 73_Netty底层架构系统总结与应用实践 74_Netty对于异步读写操作的架构思想与观察者模式的重要应用 75_适配器模式与模板方法模式...
  • 73_Netty底层架构系统总结与应用实践 74_Netty对于异步读写操作的架构思想与观察者模式的重要应用 75_适配器模式与模板方法模式在入站处理器中的应用 76_Netty项目开发过程中常见且重要事项分析 77_Java NIO Buffer...
  • 而传统关系型数据库模式固定、强调参照完整性、数据的逻辑物理形式相对独立等,比较适用于中小规模的数据,但对于数据的规模和并发读写方 面进行大规模扩展时,RDBMS性能会大大降低,分布式更为困难。 为什么...
  • 第2部分 构建并发应用程序 第6章 任务执行 6.1 在线程中执行任务 6.2 executor 框架 6.3 寻找可强化的并行性 第7章 取消和关闭 7.1 任务取消 7.2 停止基于线程的服务 7.3 处理反常的线程终止 7.4 jvm关闭 第8章 应用...
  • 到目前为止,前两篇文章已经介绍了线程安全同步的基础知识。但是我们并不希望为了获得线程安全而去分析每次内存访问;... 本篇总结自《Java并发编程实践》第四章 组合对象 章节的内容 ,详情可以查阅该书。
  • Akka入门与实践高清版

    2018-06-13 15:22:40
    Akka Java Scala 本书主要面向使 工具集来构建大规模分布式应用程序 开发 Akka 者,介绍了分布式系统的基本概念以及如何使 ...两个实例,将原理与实践结合,介绍了使 设计并实现分布式应用程序的方法。
  • Akka入门与实践

    2018-05-31 21:49:39
    第 2 章 Actor 与并发:响应式编程。Actor Future 的使用。 第 3 章 传递消息:消息传递模式。 第 4 章 Actor 的生命周期—处理状态错误:Actor 生命周期、监督机制、Stash/ Unstash、Become/Unbecome 以及有限...
  • 集群(cluster)是一组相互独立的、通过高速网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理。一个客户集群相互作用时,集群像是一个独立的服务器。负载均衡(Load Balance),其意思就是分摊到多个...
  • 1.5.1 应用和静态资源分离11 1.5.2 页面缓存12 1.5.3 集群分布式12 1.5.4 反向代理13 1.5.5 CDN14 1.6 底层的优化15 1.7 小结15 第2章 常见协议和标准17 2.1 DNS协议17 2.2 TCP/IP协议Socket18 2.3 ...
  • 业务从简单到复杂,应用也在实践中不断扩展,为了解决网站在发展过程中面临的高并发访问、海量数据处理、高可用、高可靠运行等一系列问题挑战,很多公司在实践中提出了许多的思路解决方案,这些思路解决方案在...

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 268
精华内容 107
热门标签
关键字:

并发模式与应用实践