诗剑书生的专栏

我在灌江口上住,花开花落,不知流年度.雁过空遗秋色暮,抚琴细听梧桐雨. 轻舞残虹漫展书,云卷云舒,思愫万千缕.安得婵娟与共处,长作识字耕田夫.                   诗剑书生 于灌江口.听潮居

诗剑书生ID:axman
104871次访问,排名829好友0人,关注者7
一个男人. 一个写程序男人. 一个写程序并从程序中寻找快乐的男人. 一个写程序并从程序中寻找快乐又把快乐传递给大家的男人.
一个书生. 一个寂寞的书生. 一个寂寞的梅香竹影下敲声写韵的书生. 一个寂寞的梅香竹影下敲声写韵晨钟暮鼓中逸气扬剑的书生.
那个男人是位书生。没有人知道他的姓名,居无定所,行无定踪,亦耕亦读,或渔或樵。
axman的文章
原创 87 篇
翻译 0 篇
转载 0 篇
评论 162 篇
axman的公告
最近评论
axman:不用说明什么,一切都如我预料的那样.
在整个奥运开幕式期间,我一边看电视,一边访问
http://www.cctvolympics.com/main.php?type=vod这个地址,其间访问一百多次,没有一次成功.大多数时候是前端缓存的squid在报refuse(111).有时以过几分钟的等待,能出来页面,但Flash缓冲的进度条到了99%就一直停止在那儿.
……
myvicc:写得不错,等写下文
chinagavin:我是先读三位正向输出,然后再读三位反向输出来最终达到想要的结果。
chinagavin:更新啊,好久没更新了。
YuLimin:快更新呀。。。等着呢:)呵呵。。。
文章分类
收藏
    相册
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 多线程编程的设计模式 临界区模式(一)收藏

    新一篇:  多线程编程的设计模式 临界区模式(二) | 旧一篇: Java 多线程编程中的设计模式 开篇

    多线程编程的设计模式 临界区模式(一)

    临界区模式 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
    方法安全吗?为什么?

     

    发表于 @ 2006年08月28日 08:01:00|评论(loading...)|编辑

    新一篇:  多线程编程的设计模式 临界区模式(二) | 旧一篇: Java 多线程编程中的设计模式 开篇

    评论:没有评论。

    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © axman