线程高级---异步线程

来源:互联网 发布:mac 端口映射 编辑:程序博客网 时间:2024/05/22 14:06
   异步线程是多线程编程中比较重要的编程方法。记得编写过.net的socket,提供的编程模式与传统的多线程socket编程方式不同,很长时间也没搞清楚,的确,单凭异步这个词去理解它是稍微难了些。不妨再次使用这个socket做例子来解释异步线程。
   我需要做的是编写一个服务器,接受多个客户端的请求并根据需要返回数据。.net的socket是这样来做的:首先,起一个线程listener等在那监听端口,一旦有一个客户端连接,则监听端口线程新建一个accepter线程去处理建立连接的过程,与传统不一样的地方是新建完这个线程后这个listener返回来继续监听,而不是一直做完所有的事情,同样的accepter线程再建立一个新的线程receiver去接收数据,而接收数据的线程则根据收到的数据内容判断是否给与回复,如果需要回复,则同样新建一个线程sender,让这个send去写入数据。可以看出,整个组织形式的特点就是将要做的事情交给别人去做,自己则回来继续做自己的事。比如这里的几个角色listener,accepter,receiver,sender好比四个人,listener收到连接请求后告诉accepter,这个地址来了请求,接下来交给你了,我继续回去监听;同样,accepter将接收数据交给receiver去做,而receiver交给sender去做。通过这样明确的划分,不同的线程有了不同的权责,并且每个线程都可以focus自己的工作而不是一个人挑所有的重担。这样的组织就是异步线程。
    下面来看一个简单的例子,每次我们输入一行消息,就会新建一个线程将回显的任务交给它。

/**
 * 2007-6-19
 * Input.java
 * package AsynchronizedThread
 * TODO As a class which used to do inputprocessing
 * levi
 */

package AsynchronizedThread;

import java.io.*;

public class Input {
 
 public void listen() throws Exception{
  String ch;
  BufferedReader in = newBufferedReader(new InputStreamReader(System.in));
  while(!(ch =in.readLine()).equals("EOF")){
   newOutput(ch).start();
  }
 }
}

   用来处理输入的类,注意这里没有使用新的线程来处理输入,主要方法是listen,负责监听键盘来的输入,主要逻辑每次用一个新线程Output()来处理键盘来的字符串。而每次建立线程后会接着来监听,而不是像传统线程那样一个线程做到底。这里正好复习java的IO,我们需要BufferReader的readLine()方法,但System.in却是一个inputStream,这时就需要InputStreamReader将字节流转为字符流。具体可以参考《JAVAAPI备忘----I/O》

 

/**
 * 2007-6-19
 * Output.java
 * package AsynchronizedThread
 * TODO As a asynchronized thread
 * levi
 */
package AsynchronizedThread;

public class Output extends Thread{
 
 String msg;
 
 public Output(String ch){
  msg = ch;
 }
 
 public void run(){
  System.out.println(msg);
 }
}

   这个类主要是将输入回显,非常简单,run完就死。

 

/**
 * 2007-6-19
 * Test.java
 * package AsynchronizedThread
 * TODO As main test thread
 * levi
 */

package AsynchronizedThread;

import java.io.*;

public class Test {
 public static void main(String [] args){
  Input i = new Input();
  try{
   i.listen();
  }
  catch(Exception ioe){
   
  }
 }
}

   这个类主要是一个桩。整个程序实现非常简单,这里也主要是为了说明异步而做的例子,实际中这么简单的根本不需要用异步线程。而这个例子也正好给出一个反例,什么时候需要用异步线程?当需要处理的工作是费时或者遇到需要等待的IO时,如上例中应该在Output的run方法中有这类操作时使用异步线程才是最佳选择。

   通过这个简单的例子,我们可以看出来,异步线程的好处是:高相应性,高性能。

   而异步线程有一种变形,这里稍微提一下。在Doug lee的《concurrent programming in java design principles andpatterns》中曾提到并将其称为futurepattern。这个变形跟异步线程不同的一点就是未来模式有返回值而异步线程没有。可能会很奇怪,既然有返回值,就意味着主线程要等待从线程返回值,这样就变成了异步而不是同步。这正式未来模式精妙的地方,它先从从线程返回一个值,这个值不能立即使用,因为从线程并没有做完,但是一旦从线程完成了,主线程就能得到这个值,就像现实中你用钱先去买到一张饭票,你却不能吃这张票,等到厨师做好了饭你才能拿着票去买饭。

    乍看之下觉得跟异步线程并没有区别,其实异步线程是不能有返回值的,在需要有返回值的场合可能就只会考虑到同步线程了,这样在吞吐量方面就不如未来模式,因为同步时主线程在等待,而异步时主线程却可以做自己的事情,做了一段时间再来拿那个被从线程算出来的“值”,从而提高了吞吐。下图是未来模式的类图

   这里描述未来模式的调用步骤,但不再使用程序加以说明,有兴趣可以参阅相关文档。

   Data是一个接口,定义他的主要目的是需要在Client中使用多态,由于它隐藏了实现的具体类,因此我们可以轻松的调用它的method得到结果;首先,Client调用Host的request()方法并得到结果,不幸的是,我们只能得到一个虚拟的结果,得到那张票,这个过程是通过返回Future实现的,而当Client进行了一些处理之后需要数据了,它会调用Data的method来得到结果,因为当时返回的是future,所以现在也会自动返回future的method,future自然是返回realData的method了。看到这里难免产生疑问。产生的新线程应该是在做RealData,future必须得知道RealData以便在今后能够调用RealData的method。但如何让future知道RealData呢,这里使用setRealData方法,当RealData线程的事情做完之后便会返回自己,这时就需要调用setRealData来将RealData设置进future中。最后需要注意的是应该对future的method设置循环锁以判断是否产生了真实数据,否则即使没饭还要卖给顾客就不是我们的本意了。

   future模式可以变化,比如可以使用条件锁让Client在没有产品时不是等待而是继续做自己的事;又比如可以在每个阶段产生每个阶段的返回值,实现多返回值的future。

   最后,如果你对其实现非常感兴趣,建议阅读JDK1.5中util.concurrent包中的Future和FutureTask。



转自 http://blog.sina.com.cn/s/blog_58adc9e7010009us.html


原创粉丝点击