Java多线程设计模式(6)两阶段终止模式

 Java多线程设计模式(5)Future模式
2013-05-22 10:05:27
标签: future模式  java
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://computerdragon.blog.51cto.com/6235984/1206497

一 Future Pattern

   假如有一个执行起来需要花费一些时间的方法,为了省去不必要的等待执行结果出来,继续做别的事情,则可以事先获取一个“提货单”即Future参与者,Future Pattern模式也就是这样的方式,当一个线程需要另一个线程的处理的结果时候,则不必一直等着处理结果,可以先处理别的事情,提前拿个Future对象,再过一段时间来获取另一个线程的处理结果

   在多个线程中,返回另一个线程的执行结果,最简单的就是采用主线程调用子线程后,一直无限循环等待子线程处理结果。由于这种会浪费等待的时间,且会浪费CPU,在此基础上,进而在子线程中调用主线程的方法来实现。可以利用静态方法和实例方法,在利用实例方法中就需要在调用子线程的时候通过构造器将主线程对象的实例传递给子线程使用。但是这种方式还欠缺一些灵活性,主线程的方法是由子线程调用的,至于什么时候该方法被调用则不清楚,不利于主线程处理。

    为了改变这种模式,则就利用Future Pattern,主线程调用子线程后,继续执行程序,只是在调用子线程后,暂时获取一个临时对象Future,这样在主线程想获取Future内部数据的时候,就可以调用Future的方法,如果该方法中已经有了处理结果,则此时就可以立刻获取,如果没有则主线程就需要稍微等一下返回结果。这种方式的优点就是主线程可以任何时候调用返回的结果,主线程不必长等待返回的结果。

  Future Pattern的参与者

  1 Client参与者,发送请求方,调用子线程的,它一旦发送请求后,就获取一个VirtualData,作为请求的结果。

  2 Host参与者,接受请求者,会为请求者返回一个FutureData对象。并且会启动一个线程来执行真正的任务,最后将任务的返回结果RealData对象赋值给FutureData对象。

   3 VirturalData参与者,用来统一代表FutureData参与者与RealData参与者,是他们的统一接口。

   4 FutureData参与者,它是当做工作还没有正式完成前的临时代表,会在其中进行线程的等待,唤醒,以及提供Client参与者调用处理结果的方法。

   5 RealData参与者,具体真正进行操作数据的类。

实例代码:

主要是FutureData类和Host参与者以及Client参与者,ReaLData就是真正执行的任务没写。

Client参与者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package  whut.future;
public  class  FutureMain {
     public  static  void  main(String[] args) {
      // TODO Auto-generated method stub
      System.out.println( "main BEGIN" );
      Host host= new  Host();
      Data data1=host.request( 10 'A' );
      Data data2=host.request( 20 'B' );
                    
      System.out.println( "main otherJob BEGIN" );
      try {
          Thread.sleep( 2000 );
      } catch (InterruptedException e){
      }
      System.out.println( "main otherJob END" );
                    
      System.out.println( "data1=" +data1.getContent());
      System.out.println( "data2=" +data2.getContent());
      System.out.println( "main END" );
     }
}

Host参与者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package  whut.future;
public  class  Host {
     //使用Thread-Per-Messagee Pattern
     public  Data request( final  int  count, final  char  c)
     {
         System.out.println( " request(" +count+ "," +c+ ") BEGIN" );
         final  FutureData future= new  FutureData();
         //启动线程执行
         new  Thread()
         {
             public  void  run()
             {
                 RealData realData= new  RealData(count,c);
                 future.setRealData(realData);
             }
         }.start();
         System.out.println( " request(" +count+ "," +c+ ") END" );
         return  future;
     }
}


FutureData参与者,这个是真正的关键部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package  whut.future;
//核心部分
public  class  FutureData  implements  Data {
     private  RealData realData =  null ;
     private  boolean  ready =  false ;
     public  synchronized  void  setRealData(RealData realData) {
         if  (ready)
             return ; // 利用Balk Pattern来防止赋值多次
         this .realData = realData;
         this .ready =  true ;
         notifyAll();
     }
     public  synchronized  String getContent() {
         // 运用了Guarded Suspension Pattern
         while  (!ready) {
             try  {
                 wait();
             catch  (InterruptedException e) {
             }
         }
         return  realData.getContent();
     }
}


 Java多线程设计模式(6)两阶段终止模式
2013-05-22 10:48:38
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://computerdragon.blog.51cto.com/6235984/1206548

一 Two-Phase Termination Pattern

  Two-Phase Termination Pattern,指的就是当希望结束一个线程的时候,送出一个终止请求,但是不会马上停止,做一些刷新工作。进入“终止处理中”,在该状态下,不会进行该线程日常工作任务的操作,而是进行一些终止操作。

   这个方式所考虑的因素如下

  1,必须要考虑到使得该线程能够安全的结束,Thread中的stop会有问题的,因为它会不管线程执行到哪里,都会马上停止,不能保证安全的结束。

  2,一定能够进行正常的终止处理,在java中,这点可以使用finally来实现

  3,能够高响应的终止,收到终止后,当线程在wait或者sleep或者join的时候,不用等到时间到才终止,而是马上中断线程的这些状态,进而进行终止操作。

   当一个线程正在执行周期性的工作时候,在“作业中”发了停止执行绪的请求,此时该线程不能马上离开停止,而应该先做完本次周期内部的工作,然后进入“善后阶段”完成一些善后的工作,所谓的两阶段终止,即中止“运作阶段”,并完成“善后阶段”,完整的完成执行绪的工作。

两阶段终止线程的架构模式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public  class  WorkerTerminalThread  extends  Thread {
        // 已经送出终止请求为true,初始化的时候为false
        //由于该字段可能会被多个线程访问修改,为了保护就使用这个
     private  volatile  boolean  shutdownRequested =  false ;
     // 终止请求
     public  void  shutdownRequest() {
         shutdownRequested =  true ;
         interrupt();
     }
     public  boolean  isShutdownRequest() {
         return  shutdownRequested;
     }
     // 具体动作
     public  final  void  run() {
         try  {
             while  (!shutdownRequested)
                 doWork();
         catch  (InterruptedException e) {
         }
         // 终止处理中的工作,不会进行平常操作,但是会进行终止处理
         finally  {
             doShutdown();
         }
     }
     // 具体工作操作
     private  void  doWork()  throws  InterruptedException {
     }
     // 终止后进行善后处理
     private  void  doShutdown()
     {
     }
}

解释:

  1,利用Volatile的原因是,这个字段可能会被多个线程所使用,进行修改,为了保护该字段,则可以利用同步方法或者同步代码块来保护,或者利用Volatile。用Volatile修饰的字段,强制了该成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

  2,这里运用了标识和中断状态来终止线程,之所以不单独用一个。原因是如果仅仅利用标识,无法是的那些处于wait、sleep或者join中的线程马上停止,响应性就会很差。加入了interrupt后,就可以立刻使得这些状态下的线程中断。如果仅仅利用interrupt,由于interrupt仅仅对于wait,sleep或join处进行抛出异常,如果工作代码执行在catch里,捕获了InterruptedException后,则此时interrupt就不起作用了。

本文出自 “在云端的追梦” 博客,请务必保留此出处http://computerdragon.blog.51cto.com/6235984/1206548


 Java多线程设计模式(7)线程独有储藏库模式
2013-05-22 11:48:31
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://computerdragon.blog.51cto.com/6235984/1206623

一  Thread-Specific Storage Pattern

  Thread-Specific Storage Pattern指的就是线程独有的储藏库,针对每个线程提供内存空间的意义。这种模式只有一个入口,但是内部会对每个线程提供特有的存储空间。

  Thread-Specific Storage Pattern的所有参与者:

  1,Client参与者,提出具体工作的,将该具体工作委托给TSObjectProxy参与者。

  2,TSObjectProxy参与者,这个主要是接受工作请求的,它会处理多个Client的工作,它一般都包括了TSObjectCollection。一般都是先使用TSObjectCollection参与者,取得Client参与者所对应的TSObject参与者,并将Client具体的工作委托给TSObject参与者。

  3,TSObjectCollection参与者,这个主要就是线程独有对象的集合,它利用线程键值对来存储着执行Client参与者线程所对应的TSObject参与者。一般都是利用ThreadLocal来实现。

  4,TSObject参与者,线程独有的对象,存放着线程特有的信息,尽管会为多个线程使用,但是不需要利用synchronized来进行互斥操作。

  TSObject参与者与TSObjectProxy参与者具有相同的接口。

  具体的模式如下图所示。虽然多个不同的ClientThread来请求同一个对象Log即TSObjectProxy参与者,但是Log会为每个请求线程利用ThreadLocal来分配独立的TSLog对象。所有线程所共享的是TSObjectProxy参与者。

  

关于ThreadLocal小讲:

   每一个线程都拥有自己独立的存储空间,也就是方法局部变量的堆栈,在方法里分配的局部变量都是线程所独有的,但是这些变量一退出方法就会消失了。ThreadLocal则是与方法调用无关,它为线程分配特有空间。

   ThreadLocal实例如一个保管箱间,它通过线程键值对来存储与线程特定的信息。每个线程的保管箱,都放置在ThreadLocal中。

   ThreadLocal该类提供了线程局部变量。ThreadLocal的局部变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本。

   在ThreadLocal类型的变量内部有个ThreadLocalMap,它提供了一个注册表来注册与当前线程相关的信息,ThreadLocal类型的变量对外部表现是一个,但是内部确实管理着一群对象的集合。

   在利用get与set的时候,都会在两个方法体中首先获取Thread t = Thread.currentThread();,然后在该线程的内部进行操作。不用担心其他线程对于该线程的破坏访问。在使用两个方法的时候务必要分清是属于那个线程中的操作。

   在这个模式中,客户端线程所共享的是TSObjectProxy参与者,但是实际的工作则是在TSObject参与者分配给相应的线程后才进行的,就是利用TSObjectProxy参与者来分配不被共享的TSObject参与者。对于TSObject完全不用做任何的互斥synchronized操作。

代码示例:

仅仅列出TSObjectProxy的架构模式

Log代表了TSObjectProxy参与者,供多个线程进行访问,最后将具体的操作委托给TSObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package  whut.specificstorage;
//负责产生保管箱间的类
public  class  Log {
     //首先利用static final修饰
     //保证了所有访问该类的线程都只使用一个唯一的且不变的保管箱间。
     private  static  final  ThreadLocal tsLogCollections= new  ThreadLocal();
                                         
     //不用使用synchronized,因为在使用get和set时候,会字段判别当前所属的线程
     //取得当前线程特有的Log
     private  static  TSLog getTSLog()
     {
         TSLog tsLog=(TSLog)tsLogCollections.get();
                                             
         //如果线程是第一次调用,就建立新文件夹以及注册log
         if (tsLog== null )
         {
             tsLog= new  TSLog(Thread.currentThread().getName()+ "-log.txt" );
             tsLogCollections.set(tsLog);
         }
         return  tsLog;
     }
                                         
     //加入一条LOG
     public  static  void  println(String s)
     {
            getTSLog().println(s);
     }
     //关闭LOG
     public  static  void  close()
     {
         getTSLog().close();
     }
}

   该Log类具有TSobject相同的方法,客户端线程每次请求该类,进而为请求的线程分配属于该线程的TSObject对象,每次的工作都是最终委托给TSObject来真正的执行。TSObjectProxy和TSObject类的方法与字段不需要进行任何的synchronized处理。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值