`

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

阅读更多

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

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

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

java 代码
  1. public class Geezer {   
  2.     public static void main(String[] args){   
  3.            
  4.         System.out.println("预备,开始!");   
  5.         Corrie c = new Corrie();//只有一个山洞,所以生存一个实例后传给多个Player.   
  6.         new Player("Axman","001",c).start();   
  7.         new Player("Sager","002",c).start();   
  8.         new Player("Pentium4","003",c).start();   
  9.     }   
  10. }   
  11.   

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

java 代码
  1. public class Player extends Thread{   
  2.     private final String name;   
  3.     private final String number;   
  4.     private final Corrie corrie;   
  5.     public Player(String name,String number,Corrie corrie) {   
  6.         this.name = name;   
  7.         this.number = number;   
  8.         this.corrie = corrie;   
  9.     }   
  10.        
  11.     public void run(){   
  12.         while(true){   
  13.             this.corrie.into(this.name,this.number);   
  14.         }   
  15.     }   
  16. }   

在这里,我们把成员字段都设成final的,为了说明一个Player一旦构造,他的名字和牌号就不能改
变,简单说在游戏中,我,Sager,Pentium4三个人不会自己偷偷把自己的牌号换了,也不会偷偷地去
钻别的山洞,如果这个游戏一旦发生错误,那么错误不在我们玩家.'

java 代码
  1. import java.util.*;   
  2. public class Corrie {   
  3.     private int count = 0;   
  4.     private String name;   
  5.     private String number;   
  6.     private HashMap lib = new HashMap();//保存姓名与牌号的库   
  7.        
  8.     public Corrie(){   
  9.            
  10.         lib.put("Axman","001");   
  11.         lib.put("Sager","002");   
  12.         lib.put("Pentium4","003");   
  13.     
  14.     }   
  15.        
  16.     public void into(String name,String number){   
  17.         this.count ++;   
  18.         this.name = name;   
  19.         this.number = number;   
  20.         if(this.lib.get(name).equals(number))   
  21.  test():   
  22.     }   
  23.        
  24.     public String display(){   
  25.         return this.count+": " + this.name + "(" + this.number + ")";   
  26.     }   
  27.   
  28.     private void test(){   
  29.         if(this.lib.get(name).equals(number))   
  30.             ;   
  31.             //System.out.println("OK:" + display());   
  32.         else  
  33.             System.out.println("ERR:" + display());   
  34.     }   
  35. }   
  36.   

这个类中增加了一个lib的HashMap,相当于一个玩家姓名与牌号的库,因为明知道Corrie只有一个实例,
所以我用了成员对象而不是静态实例,只是为了能在构造方法中初始化库中的内容,从真正意义中说应
该在一个辅助类中实现这样的数据结构封装的功能.如果不提供这个lib,那么在check的时候就要用

java 代码
  1. if(name.equasl("Axman")){   
  2.  if(!number.equals("001")) //出错   
  3. }   
  4. 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类为线程安全的类就行
了.其它类则不需要修改,上面说过,如果出现错误那一定不是我们玩家的事:

java 代码
  1. import java.util.*;   
  2. public class Corrie {   
  3.     private int count = 0;   
  4.     private String name;   
  5.     private String number;   
  6.     private HashMap lib = new HashMap();//保存姓名与牌号的库   
  7.        
  8.     public Corrie(){   
  9.            
  10.         lib.put("Axman","001");   
  11.         lib.put("Sager","002");   
  12.         lib.put("Pentium4","003");   
  13.     
  14.     }   
  15.        
  16.     public synchronized void into(String name,String number){   
  17.         this.count ++;   
  18.         this.name = name;   
  19.         this.number = number;   
  20.  test();   
  21.     }   
  22.        
  23.     public synchronized String display(){   
  24.         return this.count+": " + this.name + "(" + this.number + ")";   
  25.     }   
  26.   
  27.     private void test(){   
  28.         if(this.lib.get(name).equals(number))   
  29.             ;   
  30.             //System.out.println("OK:" + display());   
  31.         else  
  32.             System.out.println("ERR:" + display());   
  33.     }   
  34. }   
  35.   

 

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

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


 

分享到:
评论

相关推荐

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

    多线程编程指南

    - **第一章**:关于多线程编程,介绍了多线程的基本概念、术语以及设计技巧。 - **第二章**:线程管理,讨论了如何创建线程、配置线程属性及编写线程主体。 - **第三章**:RunLoops,深入探讨了RunLoops的工作原理...

    linux多线程编程概述.doc

    在Linux系统中,多线程编程是一种常见的编程模式,它允许多个执行流在同一进程中并发运行,从而提高程序的效率和响应性。本篇将深入介绍Linux多线程编程的基本概念、实现方法以及注意事项。 首先,多线程是通过创建...

    java多线程设计模式详解

    在深入探讨Java多线程设计模式的知识点之前,让我们首先明确多线程编程在Java中的地位和作用。Java自诞生之日起就内置了对多线程编程的支持,这使得开发能够充分利用多核处理器性能的应用程序成为可能。多线程设计...

    VC++多线程编程

    在多线程编程中,有一些常见的设计模式可以帮助开发者更好地组织代码,例如生产者-消费者模型、读写锁、工作窃取等。理解这些模式有助于编写出高效且可靠的多线程程序。 ### 7. 性能优化 多线程编程的一个主要目标...

    ASP.NET多线程编程(一)

    ### ASP.NET多线程编程(一) #### 课程概述 本课程由讲师邵志东讲解,旨在介绍ASP.NET环境下的多线程编程技术。课程针对有一定基础的学习者(Level200),要求学习者事先安装好.NET Framework以及VS.NET 2002/2003...

    多线程编程技术开发.rar

    综上所述,多线程编程技术是现代软件开发中的核心技能之一,它不仅提升了程序的执行效率,也使得程序设计更加灵活和模块化。理解和掌握多线程编程,对于开发高效、健壮的应用至关重要。通过深入学习和实践,开发者...

    win32多线程编程

    最后,多线程编程的效率和稳定性很大程度上取决于程序员对并发编程模式和线程安全机制的理解和应用。一个良好的多线程程序应该能够充分利用多核处理器的优势,同时确保数据的一致性和程序的稳定性。 因此,Win32多...

    多线程编程指南(苹果公司多线程编程指南)

    ### 多线程编程指南(苹果公司多线程编程指南) #### 重要概念与知识点 **一、关于多线程编程** ##### 1.1 什么是多线程 多线程是指在一个程序或进程中能够同时运行多个线程,每个线程执行不同的任务。通过这种...

    c#编写的多线程上位机

    9. **死锁和活锁**:在多线程编程中,必须注意潜在的死锁(两个或更多线程互相等待对方释放资源)和活锁(线程因不断重试而导致无法继续执行)问题,通过合理的资源管理和同步策略来避免。 10. **性能监控**:在...

    MFC弹球游戏(多线程编程)

    【MFC弹球游戏(多线程编程)】 在计算机编程领域,MFC(Microsoft Foundation Classes)是微软提供的一套面向对象的C++类库,它使得开发者能够更方便地利用Windows API进行Windows应用程序开发。MFC弹球游戏是利用...

    《Windows多线程编程技术与实例》-郝文化-源代码-3316

    《Windows多线程编程技术与实例》是一本深入探讨Windows平台下多线程编程的书籍,作者郝文化通过丰富的实例和源代码,为读者揭示了多线程编程的核心概念、设计模式以及实战技巧。这本书旨在帮助开发者提升在Windows...

    C++多线程编程入门

    ### C++多线程编程入门知识点详解 #### 进程与线程概念及背景 - **进程**: 进程是程序的一次动态执行过程,每个进程都有独立的内存空间和其他资源,是操作系统进行资源分配的基本单位。 - **线程**: 线程是进程中...

    Linux多线程服务端编程:使用muduo C++ 网络库.陈硕(详细书签)

    在多线程编程领域,理解和掌握其基本概念至关重要。线程是操作系统调度的基本单位,一个进程中可以包含多个线程,它们共享同一内存空间,能够并发执行任务,提高系统资源利用率和程序响应速度。书中详细阐述了创建、...

Global site tag (gtag.js) - Google Analytics