<%@ page contentType="text/html; charset=gb2312"%> 多线程编程的设计模式 临界区模式
网站公告:   ◆北天JAVA技术网热情为java爱好者服务,本网内容包括JAVA(JSP、servlet、EJB、webservice、j2ee、javabean、应用服务器、JavaScript),数据库(MYSQL、SQL Server、Sybase、Oracle、DB2、数据库综合知识),设计研究(设计模式、Struts、Spring、Hibernate、设计框架、设计综合知识),WEB2.0新技术(主要介绍AJAX),以及各种技术的入门、实例、例子等等,欢迎各位多来坐坐!◆  诚邀各位JAVA爱好者加盟!◆  本网站内容丰富,更新快,保证每周20篇以上!  
加入收藏
设为首页
联系站长
承接项目
  相关资源:网站首页 | 免费培训学院 | 技术论坛 | JAVA聊天室 | 作家专栏 | 开发工具 | 认证考试 | 会员俱乐部
  JAVA技术初学者园地 | jsp与servlet | javascript | Java源代码 | EJB | web service | 应用服务器 | JAVA综合知识
  设计研究设计模式 | 设计框架 | Struts | Spring | Hibernate | 开源项目 | 面向对象设计 | 设计综合知识
  数 据 库MYSQL | SQL Server | Sybase | Oracle | DB2 | Informix | Access | 数据库综合知识
  其他资源:AJAX新技术 | 网站开发 | ERP软件 | OA办公软件 | 商业智能BI | 开发综合知识 | 承接项目 | 项目试用

 
 
多线程编程的设计模式 临界区模式
     发布者: 发布时间:2007-02-25

临界区模式 Critical Section Pattern 是指在一个共享范围中只让一个线程执行的模式.
它是所有其它多线程设计模式的基础,所以我首先来介绍它.
把着眼点放在范围上,这个模式叫临界区模式,如果把作眼点放在执行的线程上,这个模式就叫
单线程执行模式.

首先我们来玩一个钻山洞的游戏,我 Axman,朋友 Sager,同事 Pentium4.三个人在八角游乐场
循环钻山洞(KAO,减肥训练啊),每个人手里有一个牌子,每钻一次洞口的老头会把当前的次序,
姓名,牌号显示出来,并检查名字与牌号是否一致.

OK,这个游戏的参与者有游乐场老头Geezer,Player,就是我们,还有山洞 corrie.

public class Geezer {
    public static void main(String[] args){
       
        System.out.println("预备,开始!");
        Corrie c = new Corrie();//只有一个山洞,所以生存一个实例后传给多个Player.
        new Player("Axman","001",c).start();
        new Player("Sager","002",c).start();
        new Player("Pentium4","003",c).start();
    }
}

这个类暂时没有什么多说的,它是一个Main的角色.

public class Player extends Thread{
    private final String name;
    private final String number;
    private final Corrie corrie;
    public Player(String name,String number,Corrie corrie) {
        this.name = name;
        this.number = number;
        this.corrie = corrie;
    }
   
    public void run(){
        while(true){
            this.corrie.into(this.name,this.number);
        }
    }
}
在这里,我们把成员字段都设成final的,为了说明一个Player一旦构造,他的名字和牌号就不能改
变,简单说在游戏中,我,Sager,Pentium4三个人不会自己偷偷把自己的牌号换了,也不会偷偷地去
钻别的山洞,如果这个游戏一旦发生错误,那么错误不在我们玩家.

import java.util.*;
public class Corrie {
    private int count = 0;
    private String name;
    private String number;
    private HashMap lib = new HashMap();//保存姓名与牌号的库
   
    public Corrie(){
       
        lib.put("Axman","001");
        lib.put("Sager","002");
        lib.put("Pentium4","003");
 
    }
   
    public void into(String name,String number){
        this.count ++;
        this.name = name;
        this.number = number;
        if(this.lib.get(name).equals(number))
 test():
    }
   
    public String display(){
        return this.count+": " + this.name + "(" + this.number + ")";
    }

    private void test(){
        if(this.lib.get(name).equals(number))
            ;
            //System.out.println("OK:" + display());
        else
            System.out.println("ERR:" + display());
    }
}

这个类中增加了一个lib的HashMap,相当于一个玩家姓名与牌号的库,因为明知道Corrie只有一个实例,
所以我用了成员对象而不是静态实例,只是为了能在构造方法中初始化库中的内容,从真正意义中说应
该在一个辅助类中实现这样的数据结构封装的功能.如果不提供这个lib,那么在check的时候就要用
if(name.equasl("Axman")){
 if(!number.equals("001")) //出错
}
else if .......
这样复杂的语句,如果player大多可能会写到手抽筋,所以用一个lib来chcek就非常容象.


运行这个程序需要有一些耐心,因为即使你的程序写得再差在很多单线程测试环境下也能可是正确的.
而且多线程程序在不同的机器上表现不同,要发现这个例子的错识,可能要运行很长一段时间,如果你的
机器是多CPU的,那么出现错误的机会就大好多.

在我的笔记本上最终出现错误是在11分钟以后,出现的错误有几钟情况:
1: ERR:Axman(003)
2: ERR:Sager(002)
第一种情况是检查到了错误,我的牌号明明是001,却打印出来003,而第二种明明没有错误,却打印了错误.

事实上根据以前介绍的多线程知识,不难理解这个例子的错误出现,因为into不是线程安全的,所以在其中
一个线程执行this.name = "Axman";后,本来应该执行this.numner="001",却被切换到另一个线程中执行
this.number="003",然后又经过不可预知的切换执行其中一个的if(this.lib.get(name).equals(number))
而出现1的错误,而在打印这个错误时因为display也不是线程安全的,正要打印一个错误的结果时,由于
this.name或this.number其中一个字段被修改却成了正确的匹配而出现错误2.

另外还有可能会出现序号颠倒或不对应,但这个错误我们无法直观地观察,因为你根本不知道哪个序号"应该"
给哪个Player,而序号颠倒则有可能被滚动的屏幕所掩盖.


[正确的Critical Section模式的例子]
我们知道出现这些错误是因为Corrie类的方法不是线程安全的,那么只要修改Corrie类为线程安全的类就行
了.其它类则不需要修改,上面说过,如果出现错误那一定不是我们玩家的事:

 

import java.util.*;
public class Corrie {
    private int count = 0;
    private String name;
    private String number;
    private HashMap lib = new HashMap();//保存姓名与牌号的库
   
    public Corrie(){
       
        lib.put("Axman","001");
        lib.put("Sager","002");
        lib.put("Pentium4","003");
 
    }
   
    public synchronized void into(String name,String number){
        this.count ++;
        this.name = name;
        this.number = number;
 test();
    }
   
    public synchronized String display(){
        return this.count+": " + this.name + "(" + this.number + ")";
    }

    private void test(){
        if(this.lib.get(name).equals(number))
            ;
            //System.out.println("OK:" + display());
        else
            System.out.println("ERR:" + display());
    }
}

运行这个例子,如果你的耐心,开着你的机器运行三天吧.虽然测试100天并不能说明第101天没有出错,
at least,现在的正确性比原来那个没有synchronized 保护的例子要可靠多了!

到这里我们对Critical Section模式的例程有了直观的了解,在详细解说这个模式之前,请想一下,test
方法安全吗?为什么?

(转载文章请保留出处:北天JAVA技术网(www.java114.com))
 
更多精彩文章:
AJAX真的不安全?
简单的字符转换和反转换函数
JBoss 5迎来中间件彻底的可配置时代
struts开发实践—分页的实现
Jsp+JavaBean循序渐进教程
OO设计模式和设计原则
 
最近评论:
        
你曾悄悄的来过!
wow gold,wow gold,wow gold,ffxi gil max(5544)
        
冰封的往事!
wow power leveling,wow gold,wow power leveling,wow gold max(5342)
        
冰封的往事!
wow power leveling,wow gold,WoW Gold,wow gold max(4437)
        
冰封的往事!
wow power leveling,wow gold,WoW Gold,wow gold max(7649)
        
飞舞的传奇!
传世私服,传世私服.传奇世界私服传奇世界私服,传世私服传世私服, 传奇世界私服传奇世界私服.传奇私服传奇私服. max(5140)
        
回复:多线程编程的设计模式 临界区模式
不错
        
回复:多线程编程的设计模式 临界区模式
不错
        
标 题:   
内 容:   
 
                                  
 
免责声明:该文章由网友发表,如果对您造成侵权,请联系站长

首页 - 承接项目 - 网站地图 - 联系我们 -
版权所有北天JAVA技术工作室 ICP证号:粤ICP备06079815号