随笔-204  评论-149  文章-0  trackbacks-0

我的评论

共2页: 上一页 1 2 
《设计模式》一书对Singleton模式是这样描述的:
保证一个类只有一个实例,并提供一个访问它的全局访问点。
这个模式比较简单,下面给出一个例子:
public class Singleton {
private static Singleton instance;
private Singleton(){

}
public static Singleton getInstance(){
if(instance==null)
instance=new Singleton();
return instance;
}
}
这个程序在单线程下运行不会有问题,但是它不能运行在多线程的环境下,若想让运行在多线程的环境下,必须修改如下:
public class Singleton {
private static class Instance{
static final Singleton instance=new Singleton();
}
private Singleton(){

}
public static Singleton getInstance(){
return Instance.instance;
}
}

这样做之所以可以,是因为静态的内部类Instance只会被装载一次。
package designpattern.Observer2;

import java.util.Observable;

public class Product extends Observable {
    
private String name;

    
private float price;

    
public String getName() {
        
return name;
    }


    
public void setName(String name) {
        
this.name = name;
        // 设置变化点
        setChanged();
        notifyObservers(name);

    }


    
public float getPrice() {
        
return price;
    }


    
public void setPrice(float price) {
        
this.price = price;
        // 设置变化点
        setChanged();
        notifyObservers(new Float(price));


    }


    
// 以下可以是数据库更新 插入命令.
    public void saveToDb() {

    }

}


package designpattern.Observer2;

import java.util.Observable;

//观察者NameObserver主要用来对产品名称(name)进行观察的
public class NameObserver implements java.util.Observer {

    
private String name;

    
public void update(Observable o, Object arg) {
        
if(arg instanceof String){
            name 
= (String)arg;
//            产品名称改变值在name中
            System.out.println("NameObserver :name changet to "+name);

        }


    }


}


package designpattern.Observer2;

import java.util.Observable;
import java.util.Observer;

////观察者PriceObserver主要用来对产品价格(price)进行观察的
public class PriceObserver implements Observer {

    
private float price = 0;
    
    
public void update(Observable o, Object arg) {
        
if(arg instanceof Float){
            price 
= ((Float)arg).floatValue();
            System.out.println(
"PriceObserver :price changet to "+price);
        }

    }


}


package designpattern.Observer2;

import java.util.Observer;

public class MainTest {

    
/**
     * 
@param args
     
*/

    
public static void main(String[] args) {
        Product product 
= new Product();
        
        Observer nameObs 
= new NameObserver();
        Observer priceObs 
= new PriceObserver();
        product.addObserver(nameObs);
        product.addObserver(priceObs);

        
        product.setName(
"橘子红了");
        product.setPrice(
9.26f);

    }


}

设计模式》一书对Observer是这样描述的:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并自动更新。
举个例子,在现实生活中,父母与孩子是最亲密的人。父母做为孩子(被观察者)的监护人(观察者),当孩子和别人打架后,一定会告诉他的父母这件事(呵呵,当孩子很小时,通常会告诉父母,长大了以后,可能不会,这里的孩子指的是小孩子),当孩子获得奖学金后,也一定会告诉他的父母。下面我用Observer实现这个程序。代码如下:
import java.util.Vector;

class Children{
 static private Vector<Observer> obs;
 static private String state=null;
 static{
  obs=new Vector<Observer>();
 }
 public static void attach(Observer o){
  obs.addElement(o);
 }
 public static void detach(Observer o){
  obs.removeElement(o);
 }
 public void setState(String str){
  state=str;
 }
 public String getState(){
  return state;
 }
 public void notifyObs(){
  for(Observer o:obs){
   o.update(this);
  }
 }

}
interface Observer{
 public void update(Children child);
}
class Parent implements Observer{
 public void update(Children child){
  if(child.getState().equals("fight")){
   System.out.println("Parent,他和别人打架了");
  }else if(child.getState().equals("scholarship")){
   System.out.println("告诉Parent,他得到了奖学金");
  }
 }
}
class Mother implements Observer{
 public void update(Children child){
  if(child.getState().equals("fight")){
   System.out.println("告诉Mother,他和别人打架了");
  }else if(child.getState().equals("scholarship")){
   System.out.println("告诉Mother,他得到了奖学金");
  }
 }
}
public class Client {

 public static void main(String[] args) {
  Children child=new Children();
  Observer parent=new Parent();
  Observer mother=new Mother();
  child.attach(parent);
  child.attach(mother);
  child.setState("fight");
  child.notifyObs();
  child.setState("scholarship");
  child.notifyObs();

 }

}

输出如下:
告诉Parent,他和别人打架了
告诉Mother,他和别人打架了
告诉Parent,他得到了奖学金
告诉Mother,他得到了奖学金
 小结:对于Observer模式,触发事件的对象-Subject对象无法预测可能需要知道该事件的所有对象。为了解决这一问题,我们创建一个Observer接口,要求所有的Observer负责将自己注册到Subject上。


 

一、 模式定义:
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存前的状态。
二、 模式解说
在程序运行过程中,某些对象的状态处在转换过程中,可能由于某种原因需要保存此时对象的状态,以便程序运行到某个特定阶段,需要恢复到对象之前处于某个点时的状态。如果使用一些公有接口让其它对象来得到对象的状态,便会暴露对象的实现细节。
三、 结构图
 
1) 备忘录(Memento)角色:备忘录角色存储“备忘发起角色”的内部状态。“备忘发起角色”根据需要决定备忘录角色存储“备忘发起角色”的哪些内部状态。为了防止“备忘发起角色”以外的其他对象访问备忘录。备忘录实际上有两个接口,“备忘录管理者角色”只能看到备忘录提供的窄接口——对于备忘录角色中存放的属性是不可见的。“备忘发起角色”则能够看到一个宽接口——能够得到自己放入备忘录角色中属性。
2) 备忘发起(Originator)角色:“备忘发起角色”创建一个备忘录,用以记录当前时刻它的内部状态。在需要时使用备忘录恢复内部状态。
3) 备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。
四、一个例子
这个例子是我从网上找到的,我觉得它比较形象,就拿过来直接用了。下面是这个例子的代码:
 class WindowsSystem{
 private String state;
 public Memento createMemento(){ //创建系统备份
  return new Memento(state);
 }
 public void restoreMemento(Memento m){ //恢复系统
  this.state=m.getState();
 }
 public String getState() {
  return state;
 }
 public void setState(String state) {
  this.state = state;
  System.out.println("当前系统处于"+this.state);
 }
 
}
class Memento{
 private String state;
 
 public Memento(String state) {
  this.state = state;
 }
 public String getState() {
  return state;
 }
 public void setState(String state) {
  this.state = state;
 }
}
class User{
 private Memento memento;
 public Memento retrieveMemento() {  //恢复系统
     return this.memento;
 }
 public void saveMemento(Memento memento){  //保存系统
     this.memento=memento;
 }
}

public class Test{

 public static void main(String[] args) {
 
   WindowsSystem Winxp = new WindowsSystem(); //Winxp系统
   User user = new User();   //某一用户
   Winxp.setState("好的状态");   //Winxp处于好的运行状态
   user.saveMemento(Winxp.createMemento()); //用户对系统进行备份,Winxp系统要产生备份文件
   Winxp.setState("坏的状态");   //Winxp处于不好的运行状态
   Winxp.restoreMemento(user.retrieveMemento());   //用户发恢复命令,系统进行恢复
   System.out.println("当前系统处于"+Winxp.getState());
  }

}
在本例中,WindowsSystem是发起人角色(Orignation),Memento是备忘录角色(Memento),User是备忘录管理角色(Caretaker)。Memento提供了两个接口(注意这里的接口,并不是java中的接口,它指的是可被外界调用的方法):一个是为WindowsSystem 类的宽接口,能够得到WindowsSystem放入Memento的state属性,代码见WindowsSystem的createMemento方法和restoreMemento方法,createMemento方法向Memento放入state属性,restoreMemento方法获得放入的state属性。另一个是为User类提供的窄接口,只能管理Memento而不能对它的内容进行任何操作(见User类)。
五、 优缺点
1) 保持封装边界 使用备忘录可以避免暴露一些只应由原发器管理却又必须存储在原发器之外的信息。该模式把可能很复杂的Originator内部信息对其他对象屏蔽起来,从而保持了封装边界。
2) 它简化了原发器 在其他的保持封装性的设计中,Originator负责保持客户请求过的内部状态版本。这就把所有存储管理的重任交给了Originator。让客户管理它们请求的状态将会简化Originator,并且使得客户工作结束时无需通知原发器。
3) 使用备忘录可能代价很高 如果原发器在生成备忘录时必须拷贝并存储大量的信息,或者客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销。除非封装和恢复Originator状态的开销不大,否则该模式可能并不合适。
4) 维护备忘录的潜在代价 管理器负责删除它所维护的备忘录。然而,管理器不知道备忘录中有多少个状态。因此当存储备忘录时,一个本来很小的管理器,可能会产生大量的存储开销。
六、 适用性
1)必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
2)如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
七、参考
 http://tech.it168.com/n/d/2007-05-20/200705201437328.shtml
http://www.cnblogs.com/John-zhaohui/archive/2007/08/20/862663.html
http://www.cppblog.com/converse/archive/2006/08/09/11063.html
http://java.ccidnet.com/art/3741/20030715/544777_1.html
http://blog.csdn.net/qutr/archive/2006/08/01/1007600.aspx

《设计模式》一书对Template Method模式是这样描述的:
 定义一个操作中算法的骨架,而将一些步骤延迟到子类中。不改变算法的结构而重新定义它的步骤。
 我的理解:定义一个抽象类或者说接口,在它的内部定义一些抽象的方法(供TemplateMethod调用的步骤)和一个TemplateMethod方法(非抽象方法),封装了这些抽象方法的接口或抽象类就是骨架。而将它的实现延迟到子类中,也就是用子类实现它。不改变算法的结构而重新定义它的步骤,也就是改写或者实现父类的这些非TemplateMethod的抽象方法。下面给出一个例子:
abstract class QueryTemplate{
 public void doQuery(){  //Template Method
  formatConnect();
  formatSelect();
 }
  protected abstract void formatConnect();
  protected abstract void formatSelect();
}
class OracleQT extends QueryTemplate{
 public void formatConnect() {
  System.out.println("格式化Qracle数据库连接");
 }
 public void formatSelect() {
  System.out.println("格式化Oracle数据库查询");
 }
}
class MysqlQT extends QueryTemplate{
 public void formatConnect() {
  System.out.println("格式化Mysql数据库连接");
 }
 public void formatSelect() {
  System.out.println("格式化Mysql数据库查询");
 }
}
public class client {
 public static void main(String[] args) {
  QueryTemplate oracleQT=new OracleQT();
  oracleQT.doQuery();
 
  QueryTemplate mysqlQT=new MysqlQT();
  mysqlQT.doQuery();
 }
}
输出结果:
格式化Qracle数据库连接
格式化Oracle数据库查询
格式化Mysql数据库连接
格式化Mysql数据库查询
在这个例子中,我们定义了一个骨架QueryTemplate,在它的内部定义了一个Template Method,和一些步骤(抽象方法),使用Template Method来调用这些步骤。步骤是在子类中实现的。
小结:有时候,会遇到由一系列步骤构成的过程需要执行。这个过程从高层次上看是相同的,但有些步骤的实现可能不同。正如,查询SQL数据库从高层次上看过程是相同的,但某些细节比如如何连接数据库则可能因平台等细节的不同而不同。通过Template Method模式,我们可以先定义步骤序列,然后覆盖那些需要改变的步骤。

一、FlyWeight模式定义:

运用共享技术有效地支持大量细粒度对象。

二、模式解说

也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweight(享元)模式中常出现Factory模式。Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个对象存储池(Flyweight Pool)来存放内部状态的对象。Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度。


享元模式所涉及的角色有抽象享元角色、具体(单纯)享元角色、复合享元角色、享员工厂角色,以及客户端角色等。

抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。

具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。

复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。这个角色一般很少使用。

享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。

客户端(Client)角色:本角色还需要自行存储所有享元对象的外蕴状态。

  1abstract class Character
  2  {
  3    protected char symbol;
  4    protected int width;
  5    protected int height;
  6    protected int ascent;
  7    protected int descent;
  8    protected int pointSize;
  9
 10    public abstract void Display(int pointSize);
 11  }

 12
 13  // "ConcreteFlyweight"
 14
 15  class CharacterA extends Character
 16  {
 17    // Constructor
 18    public CharacterA()
 19    {
 20      this.symbol = 'A';
 21      this.height = 100;
 22      this.width = 120;
 23      this.ascent = 70;
 24      this.descent = 0;
 25    }

 26
 27    public  void Display(int pointSize)
 28    {
 29      this.pointSize = pointSize;
 30     System.out.println(this.symbol +
 31        " (pointsize " + this.pointSize + ")");
 32    }

 33  }

 34
 35  // "ConcreteFlyweight"
 36
 37  class CharacterB extends Character
 38  {
 39    // Constructor
 40    public CharacterB()
 41    {
 42      this.symbol = 'B';
 43      this.height = 100;
 44      this.width = 140;
 45      this.ascent = 72;
 46      this.descent = 0;
 47    }

 48
 49    public  void Display(int pointSize)
 50    {
 51      this.pointSize = pointSize;
 52     System.out.println(this.symbol +
 53        " (pointsize " + this.pointSize + ")");
 54    }

 55
 56  }

 57
 58  //  C, D, E, etc.
 59
 60  // "ConcreteFlyweight"
 61
 62  class CharacterZ extends Character
 63  {
 64    // Constructor
 65    public CharacterZ()
 66    {
 67      this.symbol = 'Z';
 68      this.height = 100;
 69      this.width = 100;
 70      this.ascent = 68;
 71      this.descent = 0;
 72    }

 73
 74    public  void Display(int pointSize)
 75    {
 76      this.pointSize = pointSize;
 77      System.out.println(this.symbol +
 78        " (pointsize " + this.pointSize + ")");
 79    }

 80  }

 81  // "FlyweightFactory"
 82
 83  class CharacterFactory
 84  {
 85    private Hashtable characters = new Hashtable();
 86
 87    public Character GetCharacter(char key)
 88    {
 89      // Uses "lazy initialization"
 90      Character character = (Character)characters.get(key);
 91      if (character == null)
 92      {
 93        switch (key)
 94        {
 95          case 'A': character = new CharacterA(); break;
 96          case 'B': character = new CharacterB(); break;
 97            //
 98          case 'Z': character = new CharacterZ(); break;
 99        }

100        characters.put(key, character);
101      }

102      return character;
103    }

104  }

105// test application
106  public class Test
107  {
108    public static void main(String []args)
109    {
110      // Build a document with text
111      String document = "AAZZBBZB";
112      char[] chars = document.toCharArray();
113
114      CharacterFactory f = new CharacterFactory();
115
116      // extrinsic state
117      int pointSize = 10;
118
119      // For each character use a flyweight object
120      for(char c : chars)
121      {
122        pointSize++;
123        Character character = f.GetCharacter(c);
124        character.Display(pointSize);
125      }

126 
127      
128    }

129  }

130

五、 适用性

Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。当以下情况都成立时使用Flyweight模式。

1)  一个应用程序使用了大量的对象。

2)  完全由于使用大量的对象,造成很大的存储开销。

3)  对象的大多数状态都可变为外部状态。

4)  如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。

5)  应用程序不依赖对象标识。

六、优缺点

1)享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。

2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

http://www.jdon.com/designpatterns/flyweight.htm

Flyweight模式定义:
避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类).

为什么使用?
面向对象语言的原则就是一切都是对象,但是如果真正使用起来,有时对象数可能显得很庞大,比如,字处理软件,如果以每个文字都作为一个对象,几千个字,对象数就是几千,无疑耗费内存,那么我们还是要"求同存异",找出这些对象群的共同点,设计一个元类,封装可以被共享的类,另外,还有一些特性是取决于应用(context),是不可共享的,这也Flyweight中两个重要概念内部状态intrinsic和外部状态extrinsic之分.

说白点,就是先捏一个的原始模型,然后随着不同场合和环境,再产生各具特征的具体模型,很显然,在这里需要产生不同的新对象,所以Flyweight模式中常出现Factory模式.Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个Flyweight pool(模式池)来存放内部状态的对象.

Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度.应用场合很多:比如你要从一个数据库中读取一系列字符串,这些字符串中有许多是重复的,那么我们可以将这些字符串储存在Flyweight池(pool)中.

如何使用?


我们先从Flyweight抽象接口开始:


public interface Flyweight
{
  public void operation( ExtrinsicState state );
}

//用于本模式的抽象数据类型(自行设计)
public interface ExtrinsicState { }
 

下面是接口的具体实现(ConcreteFlyweight) ,并为内部状态增加内存空间, ConcreteFlyweight必须是可共享的,它保存的任何状态都必须是内部(intrinsic),也就是说,ConcreteFlyweight必须和它的应用环境场合无关.

public class ConcreteFlyweight implements Flyweight {
  private IntrinsicState state;
  
  public void operation( ExtrinsicState state )
  {
      //具体操作
  }
}
 

当然,并不是所有的Flyweight具体实现子类都需要被共享的,所以还有另外一种不共享的ConcreteFlyweight:

public class UnsharedConcreteFlyweight implements Flyweight {

  public void operation( ExtrinsicState state ) { }

}
 

Flyweight factory负责维护一个Flyweight池(存放内部状态),当客户端请求一个共享Flyweight时,这个factory首先搜索池中是否已经有可适用的,如果有,factory只是简单返回送出这个对象,否则,创建一个新的对象,加入到池中,再返回送出这个对象.池

public class FlyweightFactory {
  //Flyweight pool
  private Hashtable flyweights = new Hashtable();

  public Flyweight getFlyweight( Object key ) {


    Flyweight flyweight = (Flyweight) flyweights.get(key);


    if( flyweight == null ) {
      //产生新的ConcreteFlyweight
      flyweight = new ConcreteFlyweight();
      flyweights.put( key, flyweight );
    }


    return flyweight;
  }

 

至此,Flyweight模式的基本框架已经就绪,我们看看如何调用:

FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );
......


从调用上看,好象是个纯粹的Factory使用,但奥妙就在于Factory的内部设计上.

Composite模式定义:
将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性.

Composite比较容易理解,想到Composite就应该想到树形结构图。组合体内这些对象都有共同接口,当组合体一个对象的方法被调用执行时,Composite将遍历(Iterator)整个树形结构,寻找同样包含这个方法的对象并实现调用执行。可以用牵一动百来形容。

所以Composite模式使用到Iterator模式,和Chain of Responsibility模式类似。

Composite好处:
1.使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关系自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。
2.更容易在组合体内加入对象部件. 客户端不必因为加入了新的对象部件而更改代码。

如何使用Composite?
首先定义一个接口或抽象类,这是设计模式通用方式了,其他设计模式对接口内部定义限制不多,Composite却有个规定,那就是要在接口内部定义一个用于访问和管理Composite组合体的对象们(或称部件Component).

下面的代码是以抽象类定义,一般尽量用接口interface,

public abstract class Equipment
{
  private String name;
  //实价
  public abstract double netPrice();
  //折扣价格
  public abstract double discountPrice();
  //增加部件方法  
  public boolean add(Equipment equipment) { return false; }
  //删除部件方法
  public boolean remove(Equipment equipment) { return false; }
  //注意这里,这里就提供一种用于访问组合体类的部件方法。
  public Iterator iter() { return null; }
  
  public Equipment(final String name) { this.name=name; }
}

抽象类Equipment就是Component定义,代表着组合体类的对象们,Equipment中定义几个共同的方法。

public class Disk extends Equipment
{
  public Disk(String name) { super(name); }
  //定义Disk实价为1
  public double netPrice() { return 1.; }
  //定义了disk折扣价格是0.5 对折。
  public double discountPrice() { return .5; }
}

Disk是组合体内的一个对象,或称一个部件,这个部件是个单独元素( Primitive)。
还有一种可能是,一个部件也是一个组合体,就是说这个部件下面还有'儿子',这是树形结构中通常的情况,应该比较容易理解。现在我们先要定义这个组合体:

abstract class CompositeEquipment extends Equipment
{
  private int i=0;
  //定义一个Vector 用来存放'儿子'
  private Lsit equipment=new ArrayList();

  public CompositeEquipment(String name) { super(name); }

  public boolean add(Equipment equipment) {
     this.equipment.add(equipment);
     return true;
   }

  public double netPrice()
  {
    double netPrice=0.;
    Iterator iter=equipment.iterator();
    for(iter.hasNext())
      netPrice+=((Equipment)iter.next()).netPrice();
    return netPrice;
  }

  public double discountPrice()
  {
    double discountPrice=0.;
    Iterator iter=equipment.iterator();
    for(iter.hasNext())
      discountPrice+=((Equipment)iter.next()).discountPrice();
    return discountPrice;
  }
  

  //注意这里,这里就提供用于访问自己组合体内的部件方法。
  //上面dIsk 之所以没有,是因为Disk是个单独(Primitive)的元素.
  public Iterator iter()
  {
    return equipment.iterator() ;
  {
  //重载Iterator方法
   public boolean hasNext() { return i<equipment.size(); }
  //重载Iterator方法
   public Object next()
   {
    if(hasNext())
       return equipment.elementAt(i++);
    else
        throw new NoSuchElementException();
   }
  

}

上面CompositeEquipment继承了Equipment,同时为自己里面的对象们提供了外部访问的方法,重载了Iterator,Iterator是Java的Collection的一个接口,是Iterator模式的实现.

我们再看看CompositeEquipment的两个具体类:盘盒Chassis和箱子Cabinet,箱子里面可以放很多东西,如底板,电源盒,硬盘盒等;盘盒里面可以放一些小设备,如硬盘 软驱等。无疑这两个都是属于组合体性质的。

public class Chassis extends CompositeEquipment
{
   public Chassis(String name) { super(name); }
   public double netPrice() { return 1.+super.netPrice(); }
   public double discountPrice() { return .5+super.discountPrice(); }
}

public class Cabinet extends CompositeEquipment
{
   public Cabinet(String name) { super(name); }
   public double netPrice() { return 1.+super.netPrice(); }
   public double discountPrice() { return .5+super.discountPrice(); }
}

至此我们完成了整个Composite模式的架构。

我们可以看看客户端调用Composote代码:

Cabinet cabinet=new Cabinet("Tower");

Chassis chassis=new Chassis("PC Chassis");
//将PC Chassis装到Tower中 (将盘盒装到箱子里)
cabinet.add(chassis);
//将一个10GB的硬盘装到 PC Chassis (将硬盘装到盘盒里)
chassis.add(new Disk("10 GB"));

//调用 netPrice()方法;
System.out.println("netPrice="+cabinet.netPrice());
System.out.println("discountPrice="+cabinet.discountPrice());

上面调用的方法netPrice()或discountPrice(),实际上Composite使用Iterator遍历了整个树形结构,寻找同样包含这个方法的对象并实现调用执行.

Composite是个很巧妙体现智慧的模式,在实际应用中,如果碰到树形结构,我们就可以尝试是否可以使用这个模式。

装饰模式:Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这两种实体在Decorator模式中是必须的.

 

Decorator定义:
动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.

为什么使用Decorator?
我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的.

使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能.


《设计模式》一书对Decorator是这样描述的:
 动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式比生成子类更为灵活。

也就是说:动态地给对象添加一些额外的功能。它的工作原理是:创建一个始于Decorator对象(负责新功能的对象)终止于原对象的一个对象的“链”。例如,我们要为超市的收银台设计一个打印票据的程序,有的需要打印票据的头信息,有的需要打印票据的页脚信息,有的只需要打印票据的内容。如果针对每一种情况都修改一次程序,势必会很麻烦。这时我们可以考虑使用Decorator模式。其结构类图如下:
关键是Decorator既继承又组合了Component
 

代码如下:
abstract class Component{
 abstract public void printTicket();
}
class SalesTicket extends Component{
 public void printTicket() {
  System.out.println("打印出salesTicket的内容");
 }
}
abstract class TicketDecorator extends Component{
 private Component myTrailer;
 public TicketDecorator(Component myComponent){
  myTrailer=myComponent;
 }
 public void callTrailer(){
  if(myTrailer!=null)
   myTrailer.printTicket();
 }
}
class Header extends TicketDecorator{
 public Header(Component myComponent){
  super(myComponent);
 }
 public void printTicket(){
  System.out.println("打印salesTicket的头信息");
  super.callTrailer();
  
 }
}
class Footer extends TicketDecorator{
 public Footer(Component myComponent){
  super(myComponent);
 }
 public void printTicket(){
  super.callTrailer();
  System.out.println("打印salesTicket的页脚信息");
 }
}
public class Client {

 public static void main(String[] args) {
  System.out.println("====================================");
  new Header(new Footer(new SalesTicket())).printTicket();
  System.out.println("====================================");
  new Footer(new Header(new SalesTicket())).printTicket();
  System.out.println("====================================");
 }

}
输出结果如下:
====================================
打印salesTicket的头信息
打印出salesTicket的内容
打印salesTicket的页脚信息
====================================
打印salesTicket的头信息
打印出salesTicket的内容
打印salesTicket的页脚信息
====================================
从这个例子我们可以看出,Decorator模式把问题分为两部分:
1) 如何实现提供新功能的对象。
2) 如何为每种特殊情况组织对象。
这样能够将Decorator对象的实现与决定如何使用Decorator的对象分离开来,从而提高了内聚性,因为每个Decorator对象只用关心自己添加的功能,无需关心自己是如何被加入到对象链中。还可以任意地重排Decorator的顺序,无需改变其任何代码。
小结:Decorator模式的适用场合是,各种可选的功能在另一个肯定要执行的功能之前或之后执行。

Bridge模式定义 :
将抽象和行为划分开来,各自独立,但能动态的结合。

任何事物对象都有抽象和行为之分,例如人,人是一种抽象,人分男人和女人等;人有行为,行为也有各种具体表现,所以,“人”与“人的行为”两个概念也反映了抽象和行为之分。

在面向对象设计的基本概念中,对象这个概念实际是由属性和行为两个部分组成的,属性我们可以认为是一种静止的,是一种抽象,一般情况下,行为是包含在一个对象中,但是,在有的情况下,我们需要将这些行为也进行归类,形成一个总的行为接口,这就是桥模式的用处。

为什么使用?
不希望抽象部分和行为有一种固定的绑定关系,而是应该可以动态联系的。

如果一个抽象类或接口有多个具体实现(子类、concrete subclass),这些子类之间关系可能有以下两种情况:
1. 这多个子类之间概念是并列的,如前面举例,打桩,有两个concrete class:方形桩和圆形桩;这两个形状上的桩是并列的,没有概念上的重复。

2.这多个子类之中有内容概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口:抽象接口和行为接口,分别放置抽象和行为.

例如,一杯咖啡为例,子类实现类为四个:中杯加奶、大杯加奶、 中杯不加奶、大杯不加奶。

但是,我们注意到:上面四个子类中有概念重叠,可从另外一个角度进行考虑,这四个类实际是两个角色的组合:抽象 和行为,其中抽象为:中杯和大杯;行为为:加奶 不加奶(如加橙汁 加苹果汁).

实现四个子类在抽象和行为之间发生了固定的绑定关系,如果以后动态增加加葡萄汁的行为,就必须再增加两个类:中杯加葡萄汁和大杯加葡萄汁。显然混乱,扩展性极差。Bridge在一定程度上减少了子类的个数.

那我们从分离抽象和行为的角度,使用Bridge模式来实现。

如何实现?
以上面提到的咖啡 为例. 我们原来打算只设计一个接口(抽象类),使用Bridge模式后,我们需要将抽象和行为分开,加奶和不加奶属于行为,我们将它们抽象成一个专门的行为接口.

先看看抽象部分的接口代码:

public abstract class Coffee
{
   CoffeeImp coffeeImp;

   public void setCoffeeImp() {
     this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
   }

  public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}

   public abstract void pourCoffee();
}

其中CoffeeImp 是加不加奶的行为接口,看其代码如下:

public abstract class CoffeeImp
{
   public abstract void pourCoffeeImp();
}

现在我们有了两个抽象类,下面我们分别对其进行继承,实现concrete class:

//中杯
public class MediumCoffee extends Coffee
{
   public MediumCoffee() {setCoffeeImp();}

   public void pourCoffee()
   {
     CoffeeImp coffeeImp = this.getCoffeeImp();
     //我们以重复次数来说明是冲中杯还是大杯 ,重复2次是中杯
     for (int i = 0; i < 2; i++)
     {

      coffeeImp.pourCoffeeImp();
    }
  
   }
}

//大杯
public class SuperSizeCoffee extends Coffee
{
   public SuperSizeCoffee() {setCoffeeImp();}

   public void pourCoffee()
   {
     CoffeeImp coffeeImp = this.getCoffeeImp();
     //我们以重复次数来说明是冲中杯还是大杯 ,重复5次是大杯
     for (int i = 0; i < 5; i++)
     {

      coffeeImp.pourCoffeeImp();
    }
  
   }
}

上面分别是中杯和大杯的具体实现.下面再对行为CoffeeImp进行继承:

//加奶
public class MilkCoffeeImp extends CoffeeImp
{
   MilkCoffeeImp() {}

   public void pourCoffeeImp()
   {
     System.out.println("加了美味的牛奶");
   }
}

//不加奶
public class FragrantCoffeeImp extends CoffeeImp
{
   FragrantCoffeeImp() {}

   public void pourCoffeeImp()
   {
     System.out.println("什么也没加,清香");
   }
}

Bridge模式的基本框架我们已经搭好了,别忘记定义中还有一句:动态结合,我们现在可以喝到至少四种咖啡:
1.中杯加奶
2.中杯不加奶
3.大杯加奶
4.大杯不加奶

看看是如何动态结合的,在使用之前,我们做个准备工作,设计一个单态类(Singleton)用来hold当前的CoffeeImp:

public class CoffeeImpSingleton
{
   private static CoffeeImp coffeeImp;

   public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
   {this.coffeeImp = coffeeImpIn;}

   public static CoffeeImp getTheCoffeeImp()
   {
     return coffeeImp;
   }
}

看看中杯加奶 和大杯加奶 是怎么出来的:

//拿出牛奶
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());

//中杯加奶
MediumCoffee mediumCoffee = new MediumCoffee();
mediumCoffee.pourCoffee();

//大杯加奶
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
superSizeCoffee.pourCoffee();

注意: Bridge模式的执行类如CoffeeImp和Coffee是一对一的关系, 正确创建CoffeeImp是该模式的关键。


Bridge模式在EJB中的应用
EJB中有一个Data Access Object (DAO)模式,这是将商业逻辑和具体数据资源分开的,因为不同的数据库有不同的数据库操作.将操作不同数据库的行为独立抽象成一个行为接口DAO.如下:
1.Business Object (类似Coffee)

实现一些抽象的商业操作:如寻找一个用户下所有的订单

涉及数据库操作都使用DAOImplementor.

2.Data Access Object (类似CoffeeImp)

一些抽象的对数据库资源操作

3.DAOImplementor 如OrderDAOCS, OrderDAOOracle, OrderDAOSybase(类似MilkCoffeeImp FragrantCoffeeImp)

具体的数据库操作,如"INSERT INTO "等语句,OrderDAOOracle是Oracle OrderDAOSybase是Sybase数据库.

4.数据库 (Cloudscape, Oracle, or Sybase database via JDBC API)

 GOF《设计模式》一书对Facade模式是这样描述的:
       为子系统中的一组接口提供一个统一接口。Facade模式定义了一个更高层的接口,使子系统更加容易使用。

       大致意思是说:使用一种比原有方式更简单的办法与系统交互。例如,我们把一个很文件的文件,放在了第二抽屉里,而第二个抽屉的钥匙放在了第一个抽屉里,我们要想取出这个文件,第一步肯定要拿到第一个抽屉的钥匙,然后打开它再拿出第二个抽屉的钥匙,最后打开第二个抽屉取出文件。

       我就上面说的那个情形写一下实现代码,首先我们要实现二个子系统,呵呵,把抽屉比喻成系统,有点夸张了(DrawerOne、DrawerTwo):

class DrawerOne {
    public void open(){
       System.out.println("第一个抽屉被打开了");
       getKey();
    }

    public void getKey(){
       System.out.println("得到第二个抽屉的钥匙");
    }

}

class DrawerTwo{
    public void open(){
       System.out.println("第二个抽屉被打开了");
       getFile();

    }

    public void getFile(){
       System.out.println("得到这个重要文件");
    }

}

public class Client{
    public static void main(String []args){
       DrawerOne darwerOne=new DrawerOne();
       DrawerTwo darwerTwo=new DrawerTwo();
       darwerOne.open();
       darwerTwo.open();

    }

}

由于没有使用Façade模式,可以看到要想得到这个文件要首先打开第一个抽屉,然后再打开第二个抽屉,在我们实际所开发的系统中,有时候客户要实现某一操作,并不需要知道实现这一操作的详细步骤,而是简单地点击某一个按钮就可以得到自己想要的结果。下面对上面的代码使用Façade模式进行改进,建立一个FacadeDrawer类:

class DrawerFacade{

    DrawerOne darwerOne=new DrawerOne();
    DrawerTwo darwerTwo=new DrawerTwo();

    public void open(){
       darwerOne.open();
       darwerTwo.open();
    }

}

修改Client类:

public class DrawerClient{

    public static void main(String []args){
       DrawerFacade drawer=new DrawerFacade();
       drawer.open();

    }

}

输出结果如下:
第一个抽屉被打开了
得到第二个抽屉的钥匙
第二个抽屉被打开了
得到这个重要文件

正如上面所说,客户端client,它并不需要关心子系统,而是关心DrawerFacade所留下来的和外部交互的接口,而子系统在DrawerFacade的聚合。
以上只是个人拙见,哪里有不正确的地方,希望大家多多批评指正。^_^

    Facade模式主要适用于以下几种情况:

1)    不需要使用一个复杂系统的所有功能,而且可以创建一个新的类,包含访问系统的所有规则。如果只需要使用系统的部分功能,那么你为新类所创建的API将比原系统的API简单的多。

2)    希望封装或者隐藏系统原系统。

3)    希望使用原系统的功能,而且还希望增加一些新的功能。

4)    编写新类的成本小于所有人学会使用或者未来维护原系统上所需的成本。

Object类中的equals方法返回true当且仅当两个对象==为true
带hashCode和equals方法的执行结果
使用浅克隆
pt.equals(pt_clone) true
pt==pt_clone false
================================
比较pt和pt_clone的int值
0
0
修改pt_clone的int值后再比较
0
9
================================
比较pt和pt_clone的str值
pt.getStr() Hello World
pt_clone.getStr() Hello World
pt.getStr().equals(pt_clone.getStr()) true
pt.getStr()==pt_clone.getStr() true
修改pt_clone对象中的str的值后,比较pt和pt_clone的值
pt.getStr() Hello World
pt_clone.getStr() 你好,世界
pt.getStr()==pt_clone.getStr() false
===========================
比较pt和pt_clone中的temp对象的值
pt.getTemp() designpattern.Prototype.Temp@a524808d
pt_clone.getTemp() designpattern.Prototype.Temp@a524808d
pt.getTemp().equals(pt_clone.getTemp()) true
pt.getTemp()==pt_clone.getTemp() true
修改pt_clone对象的temp对象后再比较
pt.getTemp() designpattern.Prototype.Temp@a524808d
pt_clone.getTemp() designpattern.Prototype.Temp@265c0440
pt.getTemp().equals(pt_clone.getTemp()) false
pt.getTemp()==pt_clone.getTemp() false




使用深克隆方法进行创建对象
pt.getStr()==pt_deep_clone.getStr() false
pt.getTemp() designpattern.Prototype.Temp@a524808d
a524808d
pt_deep_clone.getTemp() designpattern.Prototype.Temp@a524808d
a524808d
pt.getTemp()==pt_deep_clone.getTemp() false
=================
pt designpattern.Prototype.Prototype@6b61aae8
pt_deep_clone designpattern.Prototype.Prototype@6b61aae8
6b61aae8
6b61aae8
pt==pt_deep_clone false
-1424385949
-1424385949
ab199863
true
false
true
7
Prototype模式的意图是:
 通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建出更多的同类型对象。
 在java的类库中已经实现了这一模式,只要你定义的类实现了Cloneable接口,用这个类所创建的对象可以做为原型对象进而克隆出更多的同类型的对象。下面举个例子,来介绍简单的介绍一下它的使用。
package designpattern.Prototype;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Prototype implements Serializable, Cloneable {
    
    
    
private int inttest;
    
private String str;
    
private Temp temp;
    
    
    
//浅克隆
    public Object clone() throws CloneNotSupportedException{
        Prototype prototype 
= (Prototype)super.clone();
        
return prototype;
    }

    
    
    
//深克隆
    public Object deepClone() throws IOException,ClassNotFoundException{
        
        ByteArrayOutputStream bo 
= new ByteArrayOutputStream();
        ObjectOutputStream oo 
= new ObjectOutputStream(bo);
        oo.writeObject(
this);
        
        ByteArrayInputStream bi 
= new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi 
= new ObjectInputStream(bi);
        
return oi.readObject();
    }



    
/**
     * 
@return the str
     
*/

    
public String getStr() {
        
return str;
    }



    
/**
     * 
@param str the str to set
     
*/

    
public void setStr(String str) {
        
this.str = str;
    }



    
/**
     * 
@return the temp
     
*/

    
public Temp getTemp() {
        
return temp;
    }



    
/**
     * 
@param temp the temp to set
     
*/

    
public void setTemp(Temp temp) {
        
this.temp = temp;
    }



    
/**
     * 
@return the inttest
     
*/

    
public int getInttest() {
        
return inttest;
    }



    
/**
     * 
@param inttest the inttest to set
     
*/

    
public void setInttest(int inttest) {
        
this.inttest = inttest;
    }



    
/* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     
*/

    @Override
    
public int hashCode() {
        
final int PRIME = 31;
        
int result = 1;
        result 
= PRIME * result + inttest;
        result 
= PRIME * result + ((str == null? 0 : str.hashCode());
        result 
= PRIME * result + ((temp == null? 0 : temp.hashCode());
        
return result;
    }



    
/* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     
*/

    @Override
    
public boolean equals(Object obj) {
        
if (this == obj)
            
return true;
        
if (obj == null)
            
return false;
        
if (getClass() != obj.getClass())
            
return false;
        
final Prototype other = (Prototype) obj;
        
if (inttest != other.inttest)
            
return false;
        
if (str == null{
            
if (other.str != null)
                
return false;
        }
 else if (!str.equals(other.str))
            
return false;
        
if (temp == null{
            
if (other.temp != null)
                
return false;
        }
 else if (!temp.equals(other.temp))
            
return false;
        
return true;
    }

}


package designpattern.Prototype;

import java.io.Serializable;

public class Temp implements Serializable{
    
    String test
="abce";
    
    
public Temp(String test){
        
this.test= test;
        
    }

    
    
public static void main(String[] args) throws Exception{
        
new Temp("").clone();
    }


    
/* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     
*/

    @Override
    
public int hashCode() {
        
final int PRIME = 31;
        
int result = 1;
        result 
= PRIME * result + ((test == null? 0 : test.hashCode());
        
return result;
    }


    
/* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     
*/

    @Override
    
public boolean equals(Object obj) {
        
if (this == obj)
            
return true;
        
if (obj == null)
            
return false;
        
if (getClass() != obj.getClass())
            
return false;
        
final Temp other = (Temp) obj;
        
if (test == null{
            
if (other.test != null)
                
return false;
        }
 else if (!test.equals(other.test))
            
return false;
        
return true;
    }

    
}


package designpattern.Prototype;

import java.io.IOException;


/*
 * 注意Temp和Prototye不加上equals和hashCode方法的区别
 * 
 
*/

public class TestDemo {
    
    
public static void main(String[] args) throws 
    CloneNotSupportedException,ClassNotFoundException,IOException
{
        
        Prototype pt 
= new Prototype();
        Temp temp 
= new Temp("fanghongtao");
        
//pt.setStr("Hello World");
        String str ="Hello World";
        pt.setInttest(
0);
        pt.setStr(str);
        pt.setTemp(temp);
        System.out.println(
"使用浅克隆");
        Prototype pt_clone 
= (Prototype)pt.clone();
        System.out.println(
"pt.equals(pt_clone)  "+pt.equals(pt_clone));
        System.out.println(
"pt==pt_clone   "+(pt==pt_clone));
        System.out.println(
"================================");
        System.out.println(
"比较pt和pt_clone的int值");
        System.out.println(pt.getInttest());
        System.out.println(pt_clone.getInttest());
        System.out.println(
"修改pt_clone的int值后再比较");
        pt_clone.setInttest(
9);
        System.out.println(pt.getInttest());
        System.out.println(pt_clone.getInttest());
        System.out.println(
"================================");
        System.out.println(
"比较pt和pt_clone的str值");
        System.out.println(
"pt.getStr()  "+pt.getStr());
        System.out.println(
"pt_clone.getStr()  "+pt_clone.getStr());
        System.out.println(
"pt.getStr().equals(pt_clone.getStr())  "+pt.getStr().equals(pt_clone.getStr()));
        System.out.println(
"pt.getStr()==pt_clone.getStr()  "+(pt.getStr()==pt_clone.getStr()));//竟然为true???????
        
//本来就应该为true,指向同一个String的引用,注意是引用
        
        System.out.println(
"修改pt_clone对象中的str的值后,比较pt和pt_clone的值");
        
//将pt_clone的str指向了另外一个String对象,str引用值变了
        String strnew = new String("你好,世界");
        
//pt_clone.setStr(strnew);
        pt_clone.setStr("你好,世界");
        System.out.println(
"pt.getStr()  "+pt.getStr());
        System.out.println(
"pt_clone.getStr()  "+pt_clone.getStr());
        System.out.println(
"pt.getStr()==pt_clone.getStr()  "+(pt.getStr()==pt_clone.getStr()));
        
        System.out.println(
"===========================");
        System.out.println(
"比较pt和pt_clone中的temp对象的值");
        System.out.println(
"pt.getTemp()  "+pt.getTemp());
        System.out.println(
"pt_clone.getTemp()  "+pt_clone.getTemp());
        System.out.println(
"pt.getTemp().equals(pt_clone.getTemp())  "+pt.getTemp().equals(pt_clone.getTemp()));
        System.out.println(
"pt.getTemp()==pt_clone.getTemp()  "+(pt.getTemp()==pt_clone.getTemp()));//尽然为true
        
//本来就为true,指向同一个Temp对象的引用
        System.out.println("修改pt_clone对象的temp对象后再比较");
        
        
//注意将"fanghongtao"去掉了一个'o',这样输出的designpattern.Prototype.Temp@a524808d后面的hashCode是不相同的
        
//如果还是原来的"fanghongtao"则对象实例后的hashCode相同,但是尽管hashCode相同,但是==仍然是false
        
        pt_clone.setTemp(
new Temp("fanghongta"));
        System.out.println(
"pt.getTemp()  "+pt.getTemp());
        System.out.println(
"pt_clone.getTemp()  "+pt_clone.getTemp());
        System.out.println(
"pt.getTemp().equals(pt_clone.getTemp())  "+pt.getTemp().equals(pt_clone.getTemp()));
        System.out.println(
"pt.getTemp()==pt_clone.getTemp()  "+(pt.getTemp()==pt_clone.getTemp()));
        
        
        
        System.out.println();
        System.out.println();
        System.out.println();
        System.out.println();
        
        
        System.out.println(
"使用深克隆方法进行创建对象");
        Prototype pt_deep_clone 
= (Prototype)pt.deepClone();
        
        
        System.out.println(
"pt.getStr()==pt_deep_clone.getStr()  "+(pt.getStr()==pt_deep_clone.getStr()));
        
        
        System.out.println(
"pt.getTemp()  "+pt.getTemp());//看看使用深克隆后的输入????和下面的相同
        
//designpattern.Prototype.Temp@a524808d
        System.out.println(Integer.toHexString(pt.getTemp().hashCode()));
        System.out.println(
"pt_deep_clone.getTemp()  "+pt_deep_clone.getTemp());
        System.out.println(Integer.toHexString(pt_deep_clone.getTemp().hashCode()));
        
//designpattern.Prototype.Temp@a524808d
        
//难道说明的是不能根据两个具有相同的hashCode的对象来判断其是否指向同一地址(对象)
        System.out.println("pt.getTemp()==pt_deep_clone.getTemp()  "+(pt.getTemp()==pt_deep_clone.getTemp()));
        
        
        System.out.println(
"=================");
        System.out.println(
"pt  "+pt);
        System.out.println(
"pt_deep_clone  "+pt_deep_clone);
        
//System.out.println(pt.hashCode());
        System.out.println(Integer.toHexString(pt.hashCode()));
        
//System.out.println(pt_deep_clone.hashCode());
        System.out.println(Integer.toHexString(pt_deep_clone.hashCode()));
        System.out.println(
"pt==pt_deep_clone   "+(pt==pt_deep_clone));
        
        String test_one 
="abcdef";
        String test_two 
="abcdef";
        String test_three 
= new String("abcdef");
        
//显然不能根据hashCode相同来判断两个对象==
        System.out.println(test_one.hashCode());
        System.out.println(test_three.hashCode());
        System.out.println(Integer.toHexString(test_one.hashCode()));
        System.out.println(test_one
==test_two);
        System.out.println(test_one
==test_three);
        System.out.println(test_one
==test_three.intern());
        
        System.out.println(
2^5);//异或操作符
    }


}

从上面的输出结果我们可以看出使用Object.clone()方法只能浅层次的克隆,类本身进行了克隆,即创建一个不同于原来的对象,但对象中的成员变量还是即只能对那些成员变量是基本类型进行克隆,对哪些成员变量是类类型(包括String类型)的对象进行克隆要使用到对象的序列化,不然克隆出来的Prototype对象都共享同一个temp实例。即克隆出的对象中的类类型变量仅仅克隆了引用,不会创建新的对象。

要对类类型的对象实现深层次的拷贝,则此类中的类成员变量也要implements  Cloneable接口,然后重写Object clone()方法
或者采用序列化后重写读取,就如本例子中所采用的一样

 小结:Prototype模式为我们提供另外一种高效创建对象的方法。使用Prototype模式,我们可以不了解原型对象的任何细节以及它内部的层次的结构,快速克隆出一个个同样的对象来,而且这些对象间无不影响,但是我们必须要实现它特定的接口。

@Frank_Fang
貌似这个更能说明Builder的作用
 

Builder模式定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.

Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到.

为何使用?
是为了将构建复杂对象的过程和它的部件解耦.注意: 是解耦过程部件.

因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮 方向盘 发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开.

如何使用?
首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示.

首先,需要一个接口,它定义如何创建复杂对象的各个部件:

public interface Builder {

  //创建部件A  比如创建汽车车轮
  void buildPartA();
  //创建部件B 比如创建汽车方向盘
  void buildPartB();
  //创建部件C 比如创建汽车发动机
  void buildPartC();

  //返回最后组装成品结果 (返回最后装配好的汽车)
  //成品的组装过程不在这里进行,而是转移到下面的Director类中进行.
  //从而实现了解耦过程部件
  Product getResult();

}

用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说Director的内容是如何将部件最后组装成成品:

public class Director {

  private Builder builder;

  public Director( Builder builder ) {
    this.builder = builder;
  }
  // 将部件partA partB partC最后组成复杂对象
  //这里是将车轮 方向盘和发动机组装成汽车的过程
  public void construct() {
    builder.buildPartA();
    builder.buildPartB();
    builder.buildPartC();

  }

}

Builder的具体实现ConcreteBuilder:
通过具体完成接口Builder来构建或装配产品的部件;
定义并明确它所要创建的是什么具体东西;
提供一个可以重新获取产品的接口:

public class ConcreteBuilder implements Builder {

  Part partA, partB, partC;
  public void buildPartA() {
    //这里是具体如何构建partA的代码

  };
  public void buildPartB() {
    //这里是具体如何构建partB的代码
  };
   public void buildPartC() {
    //这里是具体如何构建partB的代码
  };
   public Product getResult() {
    //返回最后组装成品结果
  };

}

复杂对象:产品Product:

public interface Product { }

复杂对象的部件:

public interface Part { }


我们看看如何调用Builder模式:


ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director( builder );

director.construct();
Product product = builder.getResult();

Builder模式的应用
在Java实际使用中,我们经常用到"池"(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池.

"池"实际是一段内存,当池中有一些复杂的资源的"断肢"(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些"断肢",将提高内存使用效率,提高池的性能.修改Builder模式中Director类使之能诊断"断肢"断在哪个部件上,再修复这个部件.

GOF《设计模式》一书对Abstract Factory模式是这样描述的:

    为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类。
  大致意思是说:我们在创建这些对象的时候,并不需要指定它们的具体类,这些具体类的对象是由工厂对象负责实例化的。下面是《Design Patterns Explained》一书的例子,有关计算机系统的显示和打印程序,用来显示和打印的分辨率取决于当前运行的系统。低端机使用低分辨率的显示和打印驱动程序,高端机使用高分辨率的显示和打印驱动程序。其结构图如下:



 1package designpattern.AbstractFactory;
 2
 3public abstract class ResFactory {
 4    abstract public DisplayDriver getDisplayDriver();
 5    abstract public PrintDriver getPrintDriver();
 6}

 7
 8
 9public class HighResFactory extends ResFactory {
10
11    @Override
12    public DisplayDriver getDisplayDriver() {
13        // TODO Auto-generated method stub
14        return new HRDD();
15    }

16
17    @Override
18    public PrintDriver getPrintDriver() {
19        // TODO Auto-generated method stub
20        return new HRPD();
21    }

22
23}

24
25
26public class LowResFactory extends ResFactory {
27
28    @Override
29    public DisplayDriver getDisplayDriver() {
30        // TODO Auto-generated method stub
31        return new LRDD();
32    }

33
34    @Override
35    public PrintDriver getPrintDriver() {
36        // TODO Auto-generated method stub
37        return new LRPD();
38    }

39
40}

41
42
43public abstract class DisplayDriver {
44    //定义其他的方法
45}

46
47public class HRDD extends DisplayDriver {
48    public HRDD(){
49        System.out.println("使用高分辨率的显示器");
50    }

51}

52public class LRDD extends DisplayDriver {
53    public LRDD(){
54        System.out.println("使用低分辨率的显示器");
55    }

56}

57
58public abstract class PrintDriver {
59    //定义其他的方法
60}

61
62public class HRPD extends PrintDriver {
63    public HRPD(){
64        System.out.println("使用高分辨率的打印机");
65    }

66}

67public class LRPD extends PrintDriver {
68    public LRPD(){
69        System.out.println("使用低分辨率的打印机");
70    }

71}

72
73public class ApControl {
74    
75    public static ResFactory getResFactory(ResFactory resFactory){
76        return resFactory;
77    }

78    
79    public static void main(String[] args){
80        
81        ResFactory highResFactory = ApControl.getResFactory(new HighResFactory());
82        DisplayDriver highDisplayDriver = highResFactory.getDisplayDriver();
83        PrintDriver highPrintDriver = highResFactory.getPrintDriver();
84        
85        ResFactory lowResFactory = ApControl.getResFactory(new LowResFactory());
86        DisplayDriver lowDisplayDriver = lowResFactory.getDisplayDriver();
87        PrintDriver lowPrintDriver = lowResFactory.getPrintDriver();
88    }

89
90}

91
92
93

输出结果:

使用高端机的显示驱动程序
使用高端机的打印驱动程序
使用低端机的显示驱动程序
使用低端机的打印驱动程序

    在这个例子中ApControl使用派生自两个不同的服务类(DisplayDriver和PrintDriver)的对象。这个设计非常简化,隐藏了实现细节,系统的可维护性也更好。ApControl不知道自己拥有的服务对象的那个特定具体实现,因为创建对象是工厂的职责。ApControl也不知道自己使用的是哪个特定工厂,因为它只知道自己有一个ResFactory对象。它可能是一个HighResFact也可能是一个LowResFact,但它不知道到底是哪一个。

    小结:在必须协调一组对象的创建时,可以应用Abstract Factory模式。它提供了一种方式,将如何执行对象实例化的规则从使用这些对象的客户对象中提取出来。首先,找出实例化的规则,定义了一个带接口的抽象类,其中的接口为每种需要实例化的对象提供一个方法。然后,从这个类为每个组实现具体类。最后,由客户对象决定使用具体工厂来创建所需的对象。它主要适用于以下几种情况:

1)     一个系统要独立于它的产品的创建、组合和表示时。
2)    可以对系统进行配置,以便系统可以使用多个产品系列中的某一个。
3)    当需要强调一系列相关产品对象的设计以便进行联合使用时。
4)    当希望提供一个产品类库,而只想显示他们的接口而不是实现时。

《设计模式》一书对Factory Method模式是这样描述的:
     定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。
     我的理解:FatoryMethod模式是一种创建型模式,定义一个用于创建对象的接口的意思是说,我们要定义一个用于创建对象的接口(或者说抽象类,实际上就是个抽象工厂abstractFactory),它的内部有一个创建对象的方法,这个方法的返回值是一个接口(或者抽象类)的类型,这个方法就是FactoryMethod;让子类决定实例化哪一个类意思是说我们要定义一个实现了该创建对象的接口(或者抽象类)的子类(具体的工厂类concreteFactory),让子类决定要创建对象的具体类型的实例(实现FactoryMethod)。下面给出一个例子,代码如下:
 1package designpattern.FactoryMethod;
 2
 3public abstract class BallFactory {
 4    protected abstract Ball makeBall();
 5}

 6
 7package designpattern.FactoryMethod;
 8
 9public class BasketBallFact extends BallFactory {
10
11    @Override
12    protected Ball makeBall() {
13        // TODO Auto-generated method stub
14        return new BasketBall();
15    }

16
17}

18
19public class FootBallFact extends BallFactory {
20
21    @Override
22    protected Ball makeBall() {
23        // TODO Auto-generated method stub
24        return new FootBall();
25    }

26
27}

28
29public class BasketBall extends Ball {
30
31    @Override
32    protected void play() {
33        System.out.println("play the baseketball");
34    }

35}

36
37public class FootBall extends Ball {
38
39    @Override
40    protected void play() {
41        System.out.println("play the football");
42    }

43
44}

45
46public abstract class Ball {
47    protected abstract void play();
48}
 1package designpattern.FactoryMethod;
 2
 3public class Client {
 4    public static void main(String[] args) {    
 5        BallFactory ballFactory = new BasketBallFact();
 6        Ball basketBall = ballFactory.makeBall();
 7        basketBall.play();
 8        
 9        ballFactory = new FootBallFact();
10        Ball footBall = ballFactory.makeBall();
11        footBall.play();
12    }

13
14}

15

小结:Factory Method模式是一个非常常用的模式。它主要适用于:
1) 当一个类不知道它所必须创建的对象的类的时候。
2) 当一个类希望由它的子类来指定它所创建的对象的时候。
3) 当类将创建对象的职责委托给多个帮助子类中的某一个,来完成这个创建对象的任务。
re: 一句话总结GOF的23种设计模式 Frank_Fang 2009-06-14 19:30  

一句话常用设计模式

Iterator——迭代器

Adapter(适配器)————换个包装再度利用

◎ 万事交给子类:

1、Template Method(模板方法)————实际处理就交给子类

2、Factory Method(工厂方法)————建立对象实例交给子类

◎建立对象实例

1、Singleton(单件)————唯一的对象实例

2、Prototype(原型)————复制建立对象实例

3、Builder(生成器)————组合复杂的对象实例

4、Abstract Factory(抽象工厂)————把相关零件组合成产品

◎切割性思考

1、Bridge(桥接)————分成功能层次和实现层次

2、Strategy(策略)————把算法整个换掉

◎一视同仁

1、Composite(组成)————对容器和内容一视同仁

2、Decorator(装饰)————对装饰和内容一视同仁

◎在结构中穿梭来去

1、Visitor(访问者)————在结构中穿梭还同时做事

2、Chain of Responsibility(职责链)————责任转送

◎简单最好

1、Facade(外观)————单一窗口

2、Mediator(中介者)————只要面对一个顾问

◎管理状态

1、Observer(观察者)————通知状态变化

2、Memento(备忘录)————存储状态

3、State(状态)————以类表示状态

◎精简不浪费

1、Flyweight(享元)————有相同的部分就分享,采取精简政策

2、Proxy(代理)————要用在建立

◎用类来表示

1、Command(命令)————将命令写成类

2、Interpreter(解释器)————以类来表达语法规则
----------------------------------------------------------------------------------

常用设计模式:


Abstract Factory:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
Prototype:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象
Factory Method:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。
Singleton:保证一个类仅有一个实例,并提供一个访问它的全局访问点
Adapter:将一个类的接口转换成客户希望的另外一个接口
Bridge:将抽象部分与实现部分分离,使它们可以独立变化
Composite:将对象组合成树形结构以表示“部分-整体”的层次结构。使客户对单个对象和复合对象的使用具有一致性
Façade:为子系统中的一组接口提供一个一致的界面
Chain of Responsibility:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求
Command:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化
Observer:定义对象间的一种一对多的依赖关系,以便当一个对象状态发生改变时,所有依赖于它的对象都得到通知并自动刷新
Template Method:定义一个操作中的算法的骨架,而将一些步骤延迟到
 

Why is Thread.stop deprecated?


Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.


Couldn't I just catch the ThreadDeath exception and fix the damaged object?


In theory, perhaps, but it would vastly complicate the task of writing correct multithreaded code. The task would be nearly insurmountable for two reasons:


  1. A thread can throw a ThreadDeath exception almost anywhere. All synchronized methods and blocks would have to be studied in great detail, with this in mind.
  2. A thread can throw a second ThreadDeath exception while cleaning up from the first (in the catch or finally clause). Cleanup would have to repeated till it succeeded. The code to ensure this would be quite complex.


In sum, it just isn't practical.


What about Thread.stop(Throwable)?


In addition to all of the problems noted above, this method may be used to generate exceptions that its target thread is unprepared to handle (including checked exceptions that the thread could not possibly throw, were it not for this method). For example, the following method is behaviorally identical to Java's throw operation, but circumvents the compiler's attempts to guarantee that the calling method has declared all of the checked exceptions that it may throw:

    static void sneakyThrow(Throwable t) {

Thread.currentThread().stop(t);
}


What should I use instead of Thread.stop?


Most uses of stop should be replaced by code that simply modifies some variable to indicate that the target thread should stop running. The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running. (This is the approach that JavaSoft's Tutorial has always recommended.) To ensure prompt communication of the stop-request, the variable must be volatile (or access to the variable must be synchronized).

For example, suppose your applet contains the following start, stop and run methods:

    private Thread blinker;

public void start() {
blinker = new Thread(this);
blinker.start();
}
public void stop() {
blinker.stop(); // UNSAFE!
}
public void run() {
Thread thisThread = Thread.currentThread();
while (true) {
try {
thisThread.sleep(interval);
} catch (InterruptedException e){
}
repaint();
}
}

You can avoid the use of Thread.stop by replacing the applet's stop and run methods with:
    private volatile Thread blinker;

public void stop() {
blinker = null;
}
public void run() {
Thread thisThread = Thread.currentThread();
while (blinker == thisThread) {
try {
thisThread.sleep(interval);
} catch (InterruptedException e){
}
repaint();
}
}


How do I stop a thread that waits for long periods (e.g., for input)?


That's what the Thread.interrupt method is for. The same "state based" signaling mechanism shown above can be used, but the state change (blinker = null, in the previous example) can be followed by a call to Thread.interrupt, to interrupt the wait:

    public void stop() {

Thread moribund = waiter;
waiter = null;
moribund.interrupt();
}

For this technique to work, it's critical that any method that catches an interrupt exception and is not prepared to deal with it immediately reasserts the exception. We say reasserts rather than rethrows, because it is not always possible to rethrow the exception. If the method that catches the InterruptedException is not declared to throw this (checked) exception, then it should "reinterrupt itself" with the following incantation:
    Thread.currentThread().interrupt();


This ensures that the Thread will reraise the InterruptedException as soon as it is able.


What if a thread doesn't respond to Thread.interrupt?


In some cases, you can use application specific tricks. For example, if a thread is waiting on a known socket, you can close the socket to cause the thread to return immediately. Unfortunately, there really isn't any technique that works in general. It should be noted that in all situations where a waiting thread doesn't respond to Thread.interrupt, it wouldn't respond to Thread.stop either. Such cases include deliberate denial-of-service attacks, and I/O operations for which thread.stop and thread.interrupt do not work properly.


Why are Thread.suspend and Thread.resume deprecated?


Thread.suspend is inherently deadlock-prone. If the target thread holds a lock on the monitor protecting a critical system resource when it is suspended, no thread can access this resource until the target thread is resumed. If the thread that would resume the target thread attempts to lock this monitor prior to calling resume, deadlock results. Such deadlocks typically manifest themselves as "frozen" processes.


What should I use instead of Thread.suspend and Thread.resume?


As with Thread.stop, the prudent approach is to have the "target thread" poll a variable indicating the desired state of the thread (active or suspended). When the desired state is suspended, the thread waits using Object.wait. When the thread is resumed, the target thread is notified using Object.notify.

For example, suppose your applet contains the following mousePressed event handler, which toggles the state of a thread called blinker:

    private boolean threadSuspended;

Public void mousePressed(MouseEvent e) {
e.consume();
if (threadSuspended)
blinker.resume();
else
blinker.suspend(); // DEADLOCK-PRONE!
threadSuspended = !threadSuspended;
}

You can avoid the use of Thread.suspend and Thread.resume by replacing the event handler above with:
    public synchronized void mousePressed(MouseEvent e) {

e.consume();
threadSuspended = !threadSuspended;
if (!threadSuspended)
notify();
}

and adding the following code to the "run loop":
                synchronized(this) {

while (threadSuspended)
wait();
}

The wait method throws the InterruptedException, so it must be inside a try ... catch clause. It's fine to put it in the same clause as the sleep. The check should follow (rather than precede) the sleep so the window is immediately repainted when the the thread is "resumed." The resulting run method follows:
    public void run() {

while (true) {
try {
Thread.currentThread().sleep(interval);
synchronized(this) {
while (threadSuspended)
wait();
}
} catch (InterruptedException e){
}
repaint();
}
}

Note that the notify in the mousePressed method and the wait in the run method are inside synchronized blocks. This is required by the language, and ensures that wait and notify are properly serialized. In practical terms, this eliminates race conditions that could cause the "suspended" thread to miss a notify and remain suspended indefinitely.

While the cost of synchronization in Java is decreasing as the platform matures, it will never be free. A simple trick can be used to remove the synchronization that we've added to each iteration of the "run loop." The synchronized block that was added is replaced by a slightly more complex piece of code that enters a synchronized block only if the thread has actually been suspended:

                if (threadSuspended) {

synchronized(this) {
while (threadSuspended)
wait();
}
}

In the absence of explicit synchronization, threadSuspended must be made volatile to ensure prompt communication of the suspend-request.


The resulting run method is:
    private boolean volatile threadSuspended;

public void run() {
while (true) {
try {
Thread.currentThread().sleep(interval);
if (threadSuspended) {
synchronized(this) {
while (threadSuspended)
wait();
}
}
} catch (InterruptedException e){
}
repaint();
}
}


Can I combine the two techniques to produce a thread that may be safely "stopped" or "suspended"?


Yes; it's reasonably straightforward. The one subtlety is that the target thread may already be suspended at the time that another thread tries to stop it. If the stop method merely sets the state variable (blinker) to null, the target thread will remain suspended (waiting on the monitor), rather than exiting gracefully as it should. If the applet is restarted, multiple threads could end up waiting on the monitor at the same time, resulting in erratic behavior.

To rectify this situation, the stop method must ensure that the target thread resumes immediately if it is suspended. Once the target thread resumes, it must recognize immediately that it has been stopped, and exit gracefully. Here's how the resulting run and stop methods look:

    public void run() {

Thread thisThread = Thread.currentThread();
while (blinker == thisThread) {
try {
thisThread.sleep(interval);
synchronized(this) {
while (threadSuspended && blinker==thisThread)
wait();
}
} catch (InterruptedException e){
}
repaint();
}
}
public synchronized void stop() {
blinker = null;
notify();
}

If the stop method calls Thread.interrupt, as described above, it needn't call notify as well, but it still must be synchronized. This ensures that the target thread won't miss an interrupt due to a race condition.


What about Thread.destroy?


Thread.destroy has never been implemented. If it were implemented, it would be deadlock-prone in the manner of Thread.suspend. (In fact, it is roughly equivalent to Thread.suspend without the possibility of a subsequent Thread.resume.) We are not implementing it at this time, but neither are we deprecating it (forestalling its implementation in future). While it would certainly be deadlock prone, it has been argued that there may be circumstances where a program is willing to risk a deadlock rather than exit outright.





5.5.2.5 Waiting on an object

A thread can wait on an object it has locked. While waiting, it releases the lock on the object and pauses until it is notified by some other thread. Another thread changes the object in some way, notifies the thread waiting on that object, and then continues. This differs from joining in that neither the waiting nor the notifying thread has to finish before the other thread can continue. Waiting is used to pause execution until an object or resource reaches a certain state. Joining is used to pause execution until a thread finishes.

Waiting on an object is one of the lesser-known ways a thread can pause. That's because it doesn't involve any methods in the Thread class. Instead, to wait on a particular object, the thread that wants to pause must first obtain the lock on the object using synchronized and then invoke one of the object's three overloaded wait( ) methods:

public final void wait( ) throws InterruptedException 
public final void wait(long milliseconds) throws InterruptedException
public final void wait(long milliseconds, int nanoseconds)
throws InterruptedException

These methods are not in the Thread class; rather, they are in the java.lang.Object class. Consequently, they can be invoked on any object of any class. When one of these methods is invoked, the thread that invoked it releases the lock on the object it's waiting on (though not any locks it possesses on other objects) and goes to sleep. It remains asleep until one of three things happens:

  • The timeout expires.

  • The thread is interrupted.

  • The object is notified.

The timeout is the same as for the sleep( ) and join( ) methods; that is, the thread wakes up after the specified amount of time has passed (within the limits of the local hardware clock accuracy). When the timeout expires, execution of the thread resumes with the statement immediately following the invocation of wait(). However, if the thread can't immediately regain the lock on the object it was waiting on, it may still be blocked for some time.

Interruption works the same way as sleep( ) and join( ): some other thread invokes the thread's interrupt( ) method. This causes an InterruptedException, and execution resumes in the catch block that catches the exception. The thread regains the lock on the object it was waiting on before the exception is thrown, however, so the thread may still be blocked for some time after the interrupt( ) method is invoked.

The third possibility, notification, is new. Notification occurs when some other thread invokes the notify( ) or notifyAll( ) method on the object on which the thread is waiting. Both of these methods are in the java.lang.Object class:

public final void notify( )
public final void notifyAll( )
These must be invoked on the object the thread was waiting on, not generally on the Thread itself. Before notifying an object, a thread must first obtain the lock on the object using a synchronized method or block. The notify( ) method selects one thread more or less at random from the list of threads waiting on the object and wakes it up. The notifyAll() method wakes up every thread waiting on the given object.
 
Once a waiting thread is notified, it attempts to regain the lock of the object it was waiting on. If it succeeds, execution resumes with the statement immediately following the invocation of wait(). If it fails, it blocks on the object until its lock becomes available; then execution resumes with the statement immediately following the invocation of wait( ).
5.5.2.4 Joining threads

It's not uncommon for one thread to need the result of another thread. For example, a web browser loading an HTML page in one thread might spawn a separate thread to retrieve every image embedded in the page. If the IMG elements don't have HEIGHT and WIDTH attributes, the main thread might have to wait for all the images to load before it can finish by displaying the page. Java provides three join( ) methods to allow one thread to wait for another thread to finish before continuing. These are:

public final void join( ) throws InterruptedException
public final void join(long milliseconds) throws InterruptedException
public final void join(long milliseconds, int nanoseconds)
throws InterruptedException

The first variant waits indefinitely for the joined thread to finish. The second two variants wait for the specified amount of time, after which they continue even if the joined thread has not finished. As with the sleep() method, nanosecond accuracy is not guaranteed.

The joining thread (that is, the one that invokes the join() method) waits for the joined thread (that is, the one whose join( ) method is invoked) to finish. For instance, consider this code fragment. We want to find the minimum, maximum, and median of a random array of doubles. It's quicker to do this with a sorted array. We spawn a new thread to sort the array, then join to that thread to await its results. Only when it's done do we read out the desired values.

double[] array = new double[10000];                         // 1
for (int i = 0; i < array.length; i++) {                  // 2
array[i] = Math.random( );                                // 3
}                                                           // 4
SortThread t = new SortThread(array);                       // 5
t.start( );                                                 // 6
try {                                                       // 7
t.join( );                                                // 8
System.out.println("Minimum: " + array[0]);               // 9
System.out.println("Median: " + array[array.length/2]);   // 10
System.out.println("Maximum: " + array[array.length-1]);  // 11
}                                                           // 12
catch (InterruptedException ex) {                           // 13
}                                                           // 14

First lines 1 through 4 execute, filling the array with random numbers. Then line 5 creates a new SortThread. Line 6 starts the thread that will sort the array. Before we can find the minimum, median, and maximum of the array, we need to wait for the sorting thread to finish. Therefore, line 8 joins the current thread to the sorting thread. At this point, the thread executing these lines of code stops in its tracks. It waits for the sorting thread to finish running. The minimum, median, and maximum are not retrieved in lines 9 through 10 until the sorting thread has finished running and died. Notice that at no point is there a reference to the thread that pauses. It's not the Thread object on which the join() method is invoked; it's not passed as an argument to that method. It exists implicitly only as the current thread. If this is within the normal flow of control of the main( ) method of the program, there may not be any Thread variable anywhere that points to this thread.

A thread that's joined to another thread can be interrupted just like a sleeping thread if some other thread invokes its interrupt( ) method. The thread experiences this invocation as an InterruptedException. From that point forward, it executes as normal, starting from the catch block that caught the exception. In the preceding example, if the thread is interrupted, it skips over the calculation of the minimum, median, and maximum because they won't be available if the sorting thread was interrupted before it could finish.

5.5.2.3 Sleeping

Sleeping is a more powerful form of yielding. Whereas yielding indicates only that a thread is willing to pause and let other equal-priority threads have a turn, a thread that goes to sleep will pause whether any other thread is ready to run or not. This gives an opportunity to run not only to other threads of the same priority but also to threads of lower priorities . However, a thread that goes to sleep does hold onto all the locks it's grabbed. Consequently, other threads that need the same locks will be blocked even if the CPU is available. Therefore, try to avoid having threads sleeping inside a synchronized method or block.

Sometimes sleeping is useful even if you don't need to yield to other threads. Putting a thread to sleep for a specified period of time lets you write code that executes once every second, every minute, every 10 minutes, and so forth. For instance, if you wrote a network monitor program that retrieved a page from a web server every five minutes and emailed the webmaster if the server had crashed, you could implement it as a thread that slept for five minutes between retrievals.

A thread goes to sleep by invoking one of two overloaded static Thread.sleep( ) methods. The first takes the number of milliseconds to sleep as an argument. The second takes both the number of milliseconds and the number of nanoseconds:

public static void sleep(long milliseconds) throws InterruptedException
public static void sleep(long milliseconds, int nanoseconds)
throws  InterruptedException

While most modern computer clocks have at least close-to-millisecond accuracy, nanosecond accuracy is rarer. There's no guarantee that you can actually time the sleep to within a nanosecond or even within a millisecond on any particular virtual machine. If the local hardware can't support that level of accuracy, the sleep time is simply rounded to the nearest value that can be measured. For example:

public void run( ) {
while (true) {
if (!getPage("http://www.cafeaulait.org/")) {
mailError("elharo@metalab.unc.edu");
}
try {
Thread.sleep(300000); // 300,000 milliseconds == 5 minutes
}
catch (InterruptedException ex) {
break;
}
}
}

The thread is not absolutely guaranteed to sleep as long as it wants to. On occasion, the thread may not be woken up until some time after its requested wake-up call, simply because the VM is busy doing other things. It is also possible that some other thread will do something to wake up the sleeping thread before its time. Generally, this is accomplished by invoking the sleeping thread's interrupt( ) method.

public void interrupt( )

This is one of those cases where the distinction between the thread and the Thread object is important. Just because the thread is sleeping doesn't mean that other threads that are awake can't work with the corresponding Thread object through its methods and fields. In particular, another thread can invoke the sleeping Thread object's interrupt( ) method, which the sleeping thread experiences as an InterruptedException. From that point forward, the thread is awake and executes as normal, at least until it goes to sleep again. In the previous example, an InterruptedException is used to terminate a thread that would otherwise run forever. When the InterruptedException is thrown, the infinite loop is broken, the run( ) method finishes, and the thread dies. The user interface thread can invoke this thread's interrupt( ) method when the user selects Exit from a menu or otherwise indicates that he wants the program to quit.

5.5.2.2 Yielding

The second way for a thread to give up control is to explicitly yield. A thread does this by invoking the static Thread.yield() method:

public static void yield( )

This signals the virtual machine that it can run another thread if one is ready to run. Some virtual machines, particularly on real-time operating systems, may ignore this hint.

Before yielding, a thread should make sure that it or its associated Runnable object is in a consistent state that can be used by other objects. Yielding does not release any locks the thread holds. Therefore, ideally, a thread should not be synchronized on anything when it yields. If the only other threads waiting to run when a thread yields are blocked because they need the synchronized resources that the yielding thread possesses, then the other threads won't be able to run. Instead, control will return to the only thread that can run, the one that just yielded, which pretty much defeats the purpose of yielding.

Making a thread yield is quite simple in practice. If the thread's run( ) method simply consists of an infinite loop, just put a call to Thread.yield( ) at the end of the loop. For example:

public void run( ) {
while (true) {
// Do the thread's work...
Thread.yield( );
}
}

This gives other threads of the same priority the opportunity to run.

If each iteration of the loop takes a significant amount of time, you may want to intersperse more calls to Thread.yield() in the rest of the code. This precaution should have minimal effect in the event that yielding isn't necessary.

5.5.2 Preemption

Every virtual machine has a thread scheduler that determines which thread to run at any given time. There are two kinds of thread scheduling: preemptive and cooperative. A preemptive thread scheduler determines when a thread has had its fair share of CPU time, pauses that thread, and then hands off control of the CPU to a different thread. A cooperative thread scheduler waits for the running thread to pause itself before handing off control of the CPU to a different thread. A virtual machine that uses cooperative thread scheduling is much more susceptible to thread starvation than a virtual machine that uses preemptive thread scheduling, since one high-priority, uncooperative thread can hog an entire CPU.

All Java virtual machines are guaranteed to use preemptive thread scheduling between priorities. That is, if a lower-priority thread is running when a higher-priority thread becomes able to run, the virtual machine will sooner or later (and probably sooner) pause the lower-priority thread to allow the higher-priority thread to run. The higher-priority thread preempts the lower-priority thread.

The situation when multiple threads of the same priority are able to run is trickier. A preemptive thread scheduler will occasionally pause one of the threads to allow the next one in line to get some CPU time. However, a cooperative thread scheduler will not. It will wait for the running thread to explicitly give up control or come to a stopping point. If the running thread never gives up control and never comes to a stopping point and if no higher-priority threads preempt the running thread, all other threads will starve. This is a bad thing. It's important to make sure all your threads periodically pause themselves so that other threads have an opportunity to run.

There are 10 ways a thread can pause in favor of other threads or indicate that it is ready to pause. These are:

  • It can block on I/O.

  • It can block on a synchronized object.

  • It can yield.

  • It can go to sleep.

  • It can join another thread.

  • It can wait on an object.

  • It can finish.

  • It can be preempted by a higher-priority thread.

  • It can be suspended.

  • It can stop.

You should inspect every run( ) method you write to make sure that one of these conditions will occur with reasonable frequency. The last two possibilities are deprecated because they have the potential to leave objects in inconsistent states, so let's look at the other eight ways a thread can be a cooperative citizen of the virtual machine.

5.5.2.1 Blocking

Blocking occurs any time a thread has to stop and wait for a resource it doesn't have. The most common way a thread in a network program will voluntarily give up control of the CPU is by blocking on I/O. Since CPUs are much faster than networks and disks, a network program will often block while waiting for data to arrive from the network or be sent out to the network. Even though it may block for only a few milliseconds, this is enough time for other threads to do significant work.

Threads can also block when they enter a synchronized method or block. If the thread does not already possess the lock for the object being synchronized on and some other thread does possess that lock, the thread will pause until the lock is released. If the lock is never released, the thread is permanently stopped.

Neither blocking on I/O nor blocking on a lock will release any locks the thread already possesses. For I/O blocks, this is not such a big deal, since eventually the I/O will either unblock and the thread will continue or an IOException will be thrown and the thread will then exit the synchronized block or method and release its locks. However, a thread blocking on a lock that it doesn't possess will never give up its own locks. If one thread is waiting for a lock that a second thread owns and the second thread is waiting for a lock that the first thread owns, deadlock results.

re: C++引用小结(转 ) Frank_Fang 2009-06-07 12:24  
#include <iostream>
using namespace std;

void LengthByValue(int array[20]) //类似于void LengthByValue(int* array)
{
cout<<sizeof(array)<<endl;
}

void LengthByRef(int (&array)[20])
{
cout<<sizeof(array)<<endl;
}

int main()
{
int array[20] = {0};
cout<<sizeof(array)<<endl;//输出80
LengthByValue(array); //输出4
LengthByRef(array); //输出80

return 0;
}
re: Java多线程小结 Frank_Fang 2009-06-02 21:52  
绿色线程

绿色线程(Green Thread)是一个相对于操作系统线程(Native Thread)的概念。
操作系统线程(Native Thread)的意思就是,程序里面的线程会真正映射到操作系统的线程(内核级线程),线程的运行和调度都是由操作系统控制的
绿色线程(Green Thread)的意思是,程序里面的线程不会真正映射到操作系统的线程,而是由语言运行平台自身来调度。
当前版本的Python语言的线程就可以映射到操作系统线程。当前版本的Ruby语言的线程就属于绿色线程,无法映射到操作系统的线程,因此Ruby语言的线程的运行速度比较慢。
难道说,绿色线程要比操作系统线程要慢吗?当然不是这样。事实上,情况可能正好相反。Ruby是一个特殊的例子。线程调度器并不是很成熟。
目前,线程的流行实现模型就是绿色线程。比如,stackless Python,就引入了更加轻量的绿色线程概念。在线程并发编程方面,无论是运行速度还是并发负载上,都优于Python。
另一个更著名的例子就是ErLang(爱立信公司开发的一种开源语言)。
ErLang的绿色线程概念非常彻底。ErLang的线程不叫Thread,而是叫做Process。这很容易和进程混淆起来。这里要注意区分一下。
ErLang Process之间根本就不需要同步。因为ErLang语言的所有变量都是final的,不允许变量的值发生任何变化。因此根本就不需要同步。
final变量的另一个好处就是,对象之间不可能出现交叉引用,不可能构成一种环状的关联,对象之间的关联都是单向的,树状的。因此,内存垃圾回收的算法效率也非常高。这就让ErLang能够达到Soft Real Time(软实时)的效果。这对于一门支持内存垃圾回收的语言来说,可不是一件容易的事情。
re: Java多线程小结 Frank_Fang 2009-06-02 21:51  
信号量

同步锁模型只是最简单的同步模型。同一时刻,只有一个线程能够运行同步代码。
有的时候,我们希望处理更加复杂的同步模型,比如生产者/消费者模型、读写同步模型等。这种情况下,同步锁模型就不够用了。我们需要一个新的模型。这就是我们要讲述的信号量模型。
信号量模型的工作方式如下:线程在运行的过程中,可以主动停下来,等待某个信号量的通知;这时候,该线程就进入到该信号量的待召(Waiting)队列当中;等到通知之后,再继续运行。
很多语言里面,同步锁都由专门的对象表示,对象名通常叫Monitor。
同样,在很多语言中,信号量通常也有专门的对象名来表示,比如,Mutex,Semphore。
信号量模型要比同步锁模型复杂许多。一些系统中,信号量甚至可以跨进程进行同步。另外一些信号量甚至还有计数功能,能够控制同时运行的线程数。
我们没有必要考虑那么复杂的模型。所有那些复杂的模型,都是最基本的模型衍生出来的。只要掌握了最基本的信号量模型——“等待/通知”模型,复杂模型也就迎刃而解了。
我们还是以Java语言为例。Java语言里面的同步锁和信号量概念都非常模糊,没有专门的对象名词来表示同步锁和信号量,只有两个同步锁相关的关键字——volatile和synchronized。
这种模糊虽然导致概念不清,但同时也避免了Monitor、Mutex、Semphore等名词带来的种种误解。我们不必执着于名词之争,可以专注于理解实际的运行原理。
在Java语言里面,任何一个Object Reference都可以作为同步锁。同样的道理,任何一个Object Reference也可以作为信号量。
Object对象的wait()方法就是等待通知,Object对象的notify()方法就是发出通知。
具体调用方法为
(1)等待某个信号量的通知
public static final Object signal = new Object();

… f1() {
synchronized(singal) { // 首先我们要获取这个信号量。这个信号量同时也是一个同步锁

// 只有成功获取了signal这个信号量兼同步锁之后,我们才可能进入这段代码
signal.wait(); // 这里要放弃信号量。本线程要进入signal信号量的待召(Waiting)队列

// 可怜。辛辛苦苦争取到手的信号量,就这么被放弃了

// 等到通知之后,从待召(Waiting)队列转到就绪(Ready)队列里面
// 转到了就绪队列中,离CPU核心近了一步,就有机会继续执行下面的代码了。
// 仍然需要把signal同步锁竞争到手,才能够真正继续执行下面的代码。命苦啊。

}
}

需要注意的是,上述代码中的signal.wait()的意思。signal.wait()很容易导致误解。signal.wait()的意思并不是说,signal开始wait,而是说,运行这段代码的当前线程开始wait这个signal对象,即进入signal对象的待召(Waiting)队列。

(2)发出某个信号量的通知
… f2() {
synchronized(singal) { // 首先,我们同样要获取这个信号量。同时也是一个同步锁。

// 只有成功获取了signal这个信号量兼同步锁之后,我们才可能进入这段代码
signal.notify(); // 这里,我们通知signal的待召队列中的某个线程。

// 如果某个线程等到了这个通知,那个线程就会转到就绪队列中
// 但是本线程仍然继续拥有signal这个同步锁,本线程仍然继续执行
// 嘿嘿,虽然本线程好心通知其他线程,
// 但是,本线程可没有那么高风亮节,放弃到手的同步锁
// 本线程继续执行下面的代码
一般的情况是signal.notify()是此段代码的最后一条语句

}
}

需要注意的是,signal.notify()的意思。signal.notify()并不是通知signal这个对象本身。而是通知正在等待signal信号量的其他线程。

以上就是Object的wait()和notify()的基本用法。
实际上,wait()还可以定义等待时间,当线程在某信号量的待召队列中,等到足够长的时间,就会等无可等,无需再等,自己就从待召队列转移到就绪队列中了。
另外,还有一个notifyAll()方法,表示通知待召队列里面的所有线程。
这些细节问题,并不对大局产生影响。
re: Java多线程小结 Frank_Fang 2009-06-02 21:51  
线程同步
作者 : buaawhl

我们可以在计算机上运行各种计算机软件程序。每一个运行的程序可能包括多个独立运行的线程(Thread)。
线程(Thread)是一份独立运行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
同步这个词是从英文synchronize(使同时发生)翻译过来的。我也不明白为什么要用这个很容易引起误解的词。既然大家都这么用,咱们也就只好这么将就。
线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

因此,关于线程同步,需要牢牢记住的第一点是:线程同步就是线程排队。同步就是排队。线程同步的目的就是避免线程“同步”执行。这可真是个无聊的绕口令。
关于线程同步,需要牢牢记住的第二点是 “共享”这两个字。只有共享资源的读写访问才需要同步。如果不是共享资源,那么就根本没有同步的必要。
关于线程同步,需要牢牢记住的第三点是,只有“变量”才需要同步访问。如果共享的资源是固定不变的,那么就相当于“常量”,线程同时读取常量也不需要同步。至少一个线程修改共享资源,这样的情况下,线程之间就需要同步。
关于线程同步,需要牢牢记住的第四点是:多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。

为了加深理解,下面举几个例子。
有两个采购员,他们的工作内容是相同的,都是遵循如下的步骤:
(1)到市场上去,寻找并购买有潜力的样品。
(2)回到公司,写报告。
这两个人的工作内容虽然一样,他们都需要购买样品,他们可能买到同样种类的样品,但是他们绝对不会购买到同一件样品,他们之间没有任何共享资源。所以,他们可以各自进行自己的工作,互不干扰。
这两个采购员就相当于两个线程;两个采购员遵循相同的工作步骤,相当于这两个线程执行同一段代码。

下面给这两个采购员增加一个工作步骤。采购员需要根据公司的“布告栏”上面公布的信息,安排自己的工作计划。
这两个采购员有可能同时走到布告栏的前面,同时观看布告栏上的信息。这一点问题都没有。因为布告栏是只读的,这两个采购员谁都不会去修改布告栏上写的信息。

下面增加一个角色。一个办公室行政人员这个时候,也走到了布告栏前面,准备修改布告栏上的信息。
如果行政人员先到达布告栏,并且正在修改布告栏的内容。两个采购员这个时候,恰好也到了。这两个采购员就必须等待行政人员完成修改之后,才能观看修改后的信息。
如果行政人员到达的时候,两个采购员已经在观看布告栏了。那么行政人员需要等待两个采购员把当前信息记录下来之后,才能够写上新的信息。
上述这两种情况,行政人员和采购员对布告栏的访问就需要进行同步。因为其中一个线程(行政人员)修改了共享资源(布告栏)。而且我们可以看到,行政人员的工作流程和采购员的工作流程(执行代码)完全不同,但是由于他们访问了同一份可变共享资源(布告栏),所以他们之间需要同步。

同步锁

前面讲了为什么要线程同步,下面我们就来看如何才能线程同步。
线程同步的基本实现思路还是比较容易理解的。我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。
生活中,我们也可能会遇到这样的例子。一些超市的外面提供了一些自动储物箱。每个储物箱都有一把锁,一把钥匙。人们可以使用那些带有钥匙的储物箱,把东西放到储物箱里面,把储物箱锁上,然后把钥匙拿走。这样,该储物箱就被锁住了,其他人不能再访问这个储物箱。(当然,真实的储物箱钥匙是可以被人拿走复制的,所以不要把贵重物品放在超市的储物箱里面。于是很多超市都采用了电子密码锁。)
线程同步锁这个模型看起来很直观。但是,还有一个严峻的问题没有解决,这个同步锁应该加在哪里?
当然是加在共享资源上了。反应快的读者一定会抢先回答。
没错,如果可能,我们当然尽量把同步锁加在共享资源上。一些比较完善的共享资源,比如,文件系统,数据库系统等,自身都提供了比较完善的同步锁机制。我们不用另外给这些资源加锁,这些资源自己就有锁。
但是,大部分情况下,我们在代码中访问的共享资源都是比较简单的共享对象。这些对象里面没有地方让我们加锁。
读者可能会提出建议:为什么不在每一个对象内部都增加一个新的区域,专门用来加锁呢?这种设计理论上当然也是可行的。问题在于,线程同步的情况并不是很普遍。如果因为这小概率事件,在所有对象内部都开辟一块锁空间,将会带来极大的空间浪费。得不偿失。
于是,现代的编程语言的设计思路都是把同步锁加在代码段上。确切的说,是把同步锁加在“访问共享资源的代码段”上。这一点一定要记住,同步锁是加在代码段上的。
同步锁加在代码段上,就很好地解决了上述的空间浪费问题。但是却增加了模型的复杂度,也增加了我们的理解难度。
现在我们就来仔细分析“同步锁加在代码段上”的线程同步模型。
首先,我们已经解决了同步锁加在哪里的问题。我们已经确定,同步锁不是加在共享资源上,而是加在访问共享资源的代码段上。
其次,我们要解决的问题是,我们应该在代码段上加什么样的锁。这个问题是重点中的重点。这是我们尤其要注意的问题:访问同一份共享资源的不同代码段,应该加上同一个同步锁;如果加的是不同的同步锁,那么根本就起不到同步的作用,没有任何意义。
这就是说,同步锁本身也一定是多个线程之间的共享对象。

Java语言的synchronized关键字

为了加深理解,举几个代码段同步的例子。
不同语言的同步锁模型都是一样的。只是表达方式有些不同。这里我们以当前最流行的Java语言为例。Java语言里面用synchronized关键字给代码段加锁。整个语法形式表现为
synchronized(同步锁) {
// 访问共享资源,需要同步的代码段
}

这里尤其要注意的就是,同步锁本身一定要是共享的对象。

… f1() {

Object lock1 = new Object(); // 产生一个同步锁

synchronized(lock1){
// 代码段 A
// 访问共享资源 resource1
// 需要同步
}
}

上面这段代码没有任何意义。因为那个同步锁是在函数体内部产生的。每个线程调用这段代码的时候,都会产生一个新的同步锁。那么多个线程之间,使用的是不同的同步锁。根本达不到同步的目的。
同步代码一定要写成如下的形式,才有意义。

public static final Object lock1 = new Object();

… f1() {

synchronized(lock1){ // lock1 是公用同步锁
// 代码段 A
// 访问共享资源 resource1
// 需要同步
}

你不一定要把同步锁声明为static或者public,但是你一定要保证相关的同步代码之间,一定要使用同一个同步锁。
讲到这里,你一定会好奇,这个同步锁到底是个什么东西。为什么随便声明一个Object对象,就可以作为同步锁?
在Java里面,同步锁的概念就是这样的。任何一个Object Reference都可以作为同步锁。我们可以把Object Reference理解为对象在内存分配系统中的内存地址。因此,要保证同步代码段之间使用的是同一个同步锁,我们就要保证这些同步代码段的synchronized关键字使用的是同一个Object Reference,同一个内存地址。这也是为什么我在前面的代码中声明lock1的时候,使用了final关键字,这就是为了保证lock1的Object Reference在整个系统运行过程中都保持不变。
一些求知欲强的读者可能想要继续深入了解synchronzied(同步锁)的实际运行机制。Java虚拟机规范中(你可以在google用“JVM Spec”等关键字进行搜索),有对synchronized关键字的详细解释。synchronized会编译成 monitor enter, … monitor exit之类的指令对。Monitor就是实际上的同步锁。每一个Object Reference在概念上都对应一个monitor。
这些实现细节问题,并不是理解同步锁模型的关键。我们继续看几个例子,加深对同步锁模型的理解。

public static final Object lock1 = new Object();

… f1() {

synchronized(lock1){ // lock1 是公用同步锁
// 代码段 A
// 访问共享资源 resource1
// 需要同步
}
}

… f2() {

synchronized(lock1){ // lock1 是公用同步锁
// 代码段 B
// 访问共享资源 resource1
// 需要同步
}
}

上述的代码中,代码段A和代码段B就是同步的。因为它们使用的是同一个同步锁lock1。
如果有10个线程同时执行代码段A,同时还有20个线程同时执行代码段B,那么这30个线程之间都是要进行同步的。
这30个线程都要竞争一个同步锁lock1。同一时刻,只有一个线程能够获得lock1的所有权,只有一个线程可以执行代码段A或者代码段B。其他竞争失败的线程只能暂停运行,进入到该同步锁的就绪(Ready)队列。
每一个同步锁下面都挂了几个线程队列,包括就绪(Ready)队列,待召(Waiting)队列等。比如,lock1对应的就绪队列就可以叫做lock1 - ready queue。每个队列里面都可能有多个暂停运行的线程。
注意,竞争同步锁失败的线程进入的是该同步锁的就绪(Ready)队列,而不是后面要讲述的待召队列(Waiting Queue,也可以翻译为等待队列)。就绪队列里面的线程总是时刻准备着竞争同步锁,时刻准备着运行。而待召队列里面的线程则只能一直等待,直到等到某个信号的通知之后,才能够转移到就绪队列中,准备运行。
成功获取同步锁的线程,执行完同步代码段之后,会释放同步锁。该同步锁的就绪队列中的其他线程就继续下一轮同步锁的竞争。成功者就可以继续运行,失败者还是要乖乖地待在就绪队列中。
因此,线程同步是非常耗费资源的一种操作。我们要尽量控制线程同步的代码段范围。同步的代码段范围越小越好。我们用一个名词“同步粒度”来表示同步代码段的范围。
同步粒度
在Java语言里面,我们可以直接把synchronized关键字直接加在函数的定义上。
比如。
… synchronized … f1() {
// f1 代码段
}

这段代码就等价于
… f1() {
synchronized(this){ // 同步锁就是对象本身
// f1 代码段
}
}

同样的原则适用于静态(static)函数
比如。
… static synchronized … f1() {
// f1 代码段
}

这段代码就等价于
…static … f1() {
synchronized(Class.forName(…)){ // 同步锁是类定义本身
// f1 代码段
}
}

但是,我们要尽量避免这种直接把synchronized加在函数定义上的偷懒做法。因为我们要控制同步粒度。同步的代码段越小越好。synchronized控制的范围越小越好。
我们不仅要在缩小同步代码段的长度上下功夫,我们同时还要注意细分同步锁。
比如,下面的代码

public static final Object lock1 = new Object();

… f1() {

synchronized(lock1){ // lock1 是公用同步锁
// 代码段 A
// 访问共享资源 resource1
// 需要同步
}
}

… f2() {

synchronized(lock1){ // lock1 是公用同步锁
// 代码段 B
// 访问共享资源 resource1
// 需要同步
}
}

… f3() {

synchronized(lock1){ // lock1 是公用同步锁
// 代码段 C
// 访问共享资源 resource2
// 需要同步
}
}

… f4() {

synchronized(lock1){ // lock1 是公用同步锁
// 代码段 D
// 访问共享资源 resource2
// 需要同步
}
}

上述的4段同步代码,使用同一个同步锁lock1。所有调用4段代码中任何一段代码的线程,都需要竞争同一个同步锁lock1。
我们仔细分析一下,发现这是没有必要的。
因为f1()的代码段A和f2()的代码段B访问的共享资源是resource1,f3()的代码段C和f4()的代码段D访问的共享资源是resource2,它们没有必要都竞争同一个同步锁lock1。我们可以增加一个同步锁lock2。f3()和f4()的代码可以修改为:
public static final Object lock2 = new Object();

… f3() {

synchronized(lock2){ // lock2 是公用同步锁
// 代码段 C
// 访问共享资源 resource2
// 需要同步
}
}

… f4() {

synchronized(lock2){ // lock2 是公用同步锁
// 代码段 D
// 访问共享资源 resource2
// 需要同步
}
}

这样,f1()和f2()就会竞争lock1,而f3()和f4()就会竞争lock2。这样,分开来分别竞争两个锁,就可以大大较少同步锁竞争的概率,从而减少系统的开销。


re: Java多线程小结 Frank_Fang 2009-06-02 14:33  
wait和notify方法的使用一定要在同步代码块中
re: java try return finally Frank_Fang 2009-05-24 23:14  
java中finally子句是怎么运行的?

看下面的程序:

public class JVMTest {

public static void main(String[] args){
System.out.println("aa:" + aa());
}
public static int aa(){
int a = 1;
int b = 10;
try{
System.out.println("abc");
return a;
}finally{
a = 2;
System.out.println("a: "+ a);
}
}
}

运行结果为:

abc
a: 2
aa:1

由此可知:在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。

在转去之前,try中先把要返回的结果存放到不同于a的局部变量中去,执行完finally之后,在从中取出返回结果,

因此,即使finally中对变量a进行了改变,但是不会影响返回结果。

但是,如果在finally子句中最后添加上return a会怎样呢?

执行结果如下:

Compiling 1 source file to E:\sun\InsideJVM\build\classes
E:\sun\InsideJVM\src\JVMTest.java:37: warning: finally clause cannot complete normally
}
1 warning
compile-single:
run-single:
abc
a: 2
aa:2

re: java try return finally Frank_Fang 2009-05-24 23:14  
主要关注一下finally中有return 或者throw时程序的执行流程
re: *虚函数表与虚表指针(转) Frank_Fang 2009-05-15 11:12  
http://blog.csdn.net/haoel/archive/2007/12/18/1948051.aspx
原文地址,其中有些东西需要斟酌
非常感谢,终于弄明白了没优化时的临时对象问题了
关于第三块注释中的
将临时对象赋给一个引用之后,这个临时对象就不会立即析构了???
---------------------------
直接的调用默认拷贝构造函数的,这个语句一共创建了多少个对象
SimpleCat constructor 0012FED8
in TheFunctionTwo tempCat指针 0012FED8
SimpleCat copy constructor0012FF30 从 0012FED8拷贝
SimpleCat destructor0012FED8
myCat3的地址是 0012FF30
myCat3.GetAge() 9999
SimpleCat destructor0012FF30



---------------------------
调用默认的赋值运算符的,这个语句一共创建了多少个对象
SimpleCat constructor 0012FF40
SimpleCat constructor 0012FEC4
in TheFunctionTwo tempCat指针 0012FEC4
SimpleCat copy constructor0012FF04 从 0012FEC4拷贝
SimpleCat destructor0012FEC4
SimpleCat重载赋值运算符0012FF40 从 0012FF04赋值
SimpleCat destructor0012FF04
myCat4.GetAge() 9999
SimpleCat destructor0012FF40


---------------------------
TheFunctionTwo()返回的临时对象赋给一个引用
SimpleCat constructor 0012FED4
in TheFunctionTwo tempCat指针 0012FED4
SimpleCat copy constructor0012FF50 从 0012FED4拷贝
SimpleCat destructor0012FED4
打印一下返回的临时对象的地址,临时对象赋给引用之后就不会立即析构了---0012FF50
rmCat.GetAge() 9999
SimpleCat destructor0012FF50
有点搞不明白?有谁知道细节的发个评论,谢谢!!
re: 上一篇C++虚函数例子 Frank_Fang 2009-05-08 10:46  
A constructor
100
100

A constructor
B constructor
in A
non virtual fun in B
4
B destructor
A destructor
----------------

A constructor
B constructor
in A
non virtual fun in A
B destructor
A destructor

A constructor
B constructor
A constructor
B1 constructor
sub12 const
4
4
4
4
4
SubB12::B::getAge()---4
SubB12::B::A::getAge()----4
*******
non virtual fun in SubB12
向上转型
non virtual fun in B
向下转型是怎么做的?
non virtual fun in SubB12
abbs调的是谁的?
non virtual fun in A
---这个的向下转型为什么不能成功

A constructor
B constructor
sub22 const
abs调的是谁的?
non virtual fun in A
---这个的向下转型
non virtual fun in SubB22
sub22 desco
B destructor
A destructor
----------------------


测试虚函数开始
A constructor
B constructor
A constructor
B1 constructor
sub12 const
virtual fun in B
A constructor
B constructor
A constructor
B1 constructor
sub12 const
virtual fun in B
A constructor
B constructor
A constructor
B1 constructor
sub12 const
virtual fun in B1
A constructor
B constructor
virtual fun in B
A constructor
B constructor
sub22 const
这个虚函数调的是谁的方法
virtual fun in B
sub12 desco
B1 destructor
A destructor
B destructor
A destructor

A destructor
请按任意键继续. . .
re: C++ 友元类和友元函数 Frank_Fang 2009-05-07 15:36  
让我们回顾一下重载的等于操作符的定义它是为名字空间域中定义的
  String 类而提供的针对两个String 对象的等于操作符如下
  bool operator==( const String &str1, const String &str2 )
  {
  if ( str1.size() != str2.size() )
  return false;
  return strcmp( str1.c_str(), str2.c_str() ) ? false : true;
  }
  把这个定义与被定义为成员函数的操作符定义相比较
  bool String::operator==( const String &rhs ) const
  {
  if ( _size != rhs._size )
  return false;
  return strcmp( _string, rhs._string ) ? false : true;
  }
  你看到区别了吗我们注意到必须要修改函数定义内部对于String 类私有数据成员的引
  用方式因为新的等于操作符是全局函数不是类成员函数它不能直接引用String 的私有
  数据成员它使用访问成员函数size()和c_str()来获得String 对象的大小以及底层的C 风格
  字符串
  另外一种可能的实现是把全局等于操作符声明为String 类的友元friend 通过把函
  数或操作符声明为友元一个类可以授予这个函数或操作符访问其非公有成员的权利
  友元声明以关键字friend 开始它只能出现在类定义中因为友元不是授权类的成员
  所以它不受其所在类的声明区域public private 和protected 的影响这里我们选择把所
  有友元声明组织在一起并放在类头之后
  class String {
  friend bool operator==( const String &, const String & );
  friend bool operator==( const char *, const String & );
  friend bool operator==( const String &, const char * );
  public:
  // ... String 类中的其他部分
  };
  String 类中的三个友元声明把全局域中声明的三个重载的比较操作符在上节介绍声
  明为String 类的友元
  既然这些等于操作符已经被声明为友元那么它们的定义就可以直接引用String 的私有
  成员了
  // friend 操作符直接引用 String 的私有成员
  // friend operators: refer to String private members directly
  bool operator==( const String &str1, const String &str2 )
  {
  if ( str1._size != str2._size )
  return false;
  return strcmp( str1._string, str2._string ) ? false : true;
  }
  inline bool operator==( const String &str, const char *s )
  {
  return strcmp( str._string, s ) ? false : true;
  }
  // 以下略
  有人可能会说在这种情况下由于c_str()和size()是内联的它们提供了等价的效率
  并且保留了成员封装所以没必要直接访问_size 和_string 这是对的使用成员访问函数而
  不是直接访问成员并不总是意味着它的效率较低由于存在这些访问函数所以没有必要
  把等于操作符声明为String 类的友元
  那么我们怎样判断一个非类成员的操作符应该是类的友元还是应该使用成员访问函
  数呢一般来说类的实现者应该尽量使得名字空间函数和访问类内部表示的操作符的数目
  最小化如果已经提供了访问成员函数并且它们具有等同的效率那么最好是使用这些成员
  函数并且把名字空间操作符与类表示中的变化隔离开但是如果类的实现者决定不为该
  类的某些私有成员提供访问成员函数而且名字空间操作符需要引用这些私有成员才能完成
  它们的操作那么就必须使用友元机制
  友元声明的最常见用法是允许非成员的重载操作符访问一个视其为朋友的类的私
  有成员原因是除了提供左和右操作数的对称性外非成员的重载操作符就像成员函数一
  样能够完全访问一个类的私有成员
re: 转:Axis2新一篇入门教程 Frank_Fang 2009-05-05 10:57  
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<RevokeCertResponse xmlns="http://test.raw.my/">
<RevokeDate>2007-01-01T00:00:00.000Z</RevokeDate>
</RevokeCertResponse>
</soapenv:Body>
</soapenv:Envelope>
re: 转:Axis2新一篇入门教程 Frank_Fang 2009-05-05 10:57  
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<RevokeCertRequest xmlns="http://test.raw.my">
<Issuer>C=JP,O=FX,CN=SDES CA</Issuer>
<Serial>1234567890</Serial>
<RevocationDate>2007-01-01T00:00:00.000Z</RevocationDate>
</RevokeCertRequest>
</soapenv:Body>
</soapenv:Envelope>
re: 转:Axis2新一篇入门教程 Frank_Fang 2009-05-05 10:51  
QName and Namespace
QName对象表示了一个XML元素或属性的qualified name,即一个namespace和一个local name的二元组。
Namespace对象表示了QName二元组中的namespace部分,它由prefix和URI两部分组成。
prefix :前缀,其它就是Name与Vaule中的Name。也可以说是URI的简写的标识。
URI:统一资源标识符 (Uniform Resource Identifier, URI) ,这里定义这样一个URI,并没有实际远程意义,只是你取的字符串代表了你的命名含义。
/******************** SAMPLE XML FILE *************************
<heavyz:Sample
xmlns:heavyz="http://www.heavyzheng.com/schema/sample.xsd">" target="_new">http://www.heavyzheng.com/schema/sample.xsd">
<heavyz:HelloWorld/>
</heavyz:Sample>
*************************************************************/

public void printRootQNameInfo(Document doc) {
Element root = doc.getRootElement();
QName qname = root.getQName();
System.out.println("local name : " + qname.getName());
System.out.println("namespace prefix: " + qname.getNamespacePrefix());
System.out.println("namespace URI : " + qname.getNamespaceURI());
System.out.println("qualified name : " + qname.getQualifiedName());
return;
}

/************************* OUTPUT *****************************
localname : Sample
namespace prefix: heavyz
namespace URI : http://www.heavyzheng.com/schema/sample.xsd
qualified name : heavyz:Sample
*************************************************************/
axis2.1.4 中客户端采用的是ServiceClient,没有Call
具体的区别参看最新axis2 API
re: Java对象的强、软、弱和虚引用 Frank_Fang 2009-05-04 10:09  

1. Java 2 引用类使用指南
... 与垃圾收集器(garbage collector)之间有限的交互。Peter Haggar 在本文中分析了 SoftReference 、 WeakReference 和 PhantomReference 类的功能和行为,并就这些类的使用给出了 ... 引用类使用指南 学习如何有效地使用 SoftReference、WeakReference 和 PhantomReference ... Peter Haggar 在本文中分析了 SoftReference 、 WeakReference 和 PhantomReference ... 平台中首次引入 java.lang.ref 包(其中包含 SoftReference 、 WeakReference 和 PhantomReference ... 2003年1月8日

2. 垃圾收集器与Java编程
将它设置为soft 引用类型,并且释放强引用; SoftReference sr=new SoftReference(image); image=null; … //下次使用时 ... 因此需要重新装载; image=new Image(); sr=new SoftReference(image); } 其他公司、产品或服务的名称可能是其他公司的商标或服务标志 ... 2002年11月16日

3. Java 理论和实践: 用软引用阻止内存泄漏
但是可以选择如何使用软引用。您可以将缓存作为 Map<K, SoftReference<V>> 或 SoftReference<Map<K,V>> 管理。后一种选项通常更好一些 ... public class CachingChecksum { private SoftReference<byte[]> bufferRef; public synchronized int ... 2006年3月20日

4. Java 理论与实践: JVM 1.4.1 中的垃圾收集
时得到的一些 经验,包括标记-清除和标记-清除-整理垃圾收集的细节。 引用对象,如 WeakReference 和 SoftReference ,加入了对确定对象是否可以被垃圾收集的额外考虑。 关于作者 对本文的评价 其他公司 ...
re: Java对象的强、软、弱和虚引用 Frank_Fang 2009-04-30 18:11  
Just created: soft 0-------------java.lang.ref.SoftReference@c17164
Just created: soft 1-------------java.lang.ref.SoftReference@1fb8ee3
Just created: soft 2-------------java.lang.ref.SoftReference@61de33
Just created: soft 3-------------java.lang.ref.SoftReference@14318bb
Just created: soft 4-------------java.lang.ref.SoftReference@ca0b6
Just created: soft 5-------------java.lang.ref.SoftReference@10b30a7
Just created: soft 6-------------java.lang.ref.SoftReference@1a758cb
Just created: soft 7-------------java.lang.ref.SoftReference@1b67f74
Just created: soft 8-------------java.lang.ref.SoftReference@69b332
Just created: soft 9-------------java.lang.ref.SoftReference@173a10f
end softreference
Just created: weak 0------java.lang.ref.WeakReference@a62fc3
Just created: weak 1------java.lang.ref.WeakReference@89ae9e
Just created: weak 2------java.lang.ref.WeakReference@1270b73
Just created: weak 3------java.lang.ref.WeakReference@60aeb0
Just created: weak 4------java.lang.ref.WeakReference@16caf43
Just created: weak 5------java.lang.ref.WeakReference@66848c
Just created: weak 6------java.lang.ref.WeakReference@8813f2
Just created: weak 7------java.lang.ref.WeakReference@1d58aae
Just created: weak 8------java.lang.ref.WeakReference@83cc67
Just created: weak 9------java.lang.ref.WeakReference@e09713
Finalizing weak 6
Finalizing weak 9
Finalizing weak 8
Finalizing weak 7
Finalizing weak 5
Finalizing weak 4
Finalizing weak 3
Finalizing weak 2
Finalizing weak 1
Finalizing weak 0
In queue: java.lang.ref.WeakReference@83cc67 : null
end softreference
Just created: null-----java.lang.ref.PhantomReference@10d448
Just created: null-----java.lang.ref.PhantomReference@e0e1c6
Just created: null-----java.lang.ref.PhantomReference@6ca1c
Just created: null-----java.lang.ref.PhantomReference@1bf216a
Just created: null-----java.lang.ref.PhantomReference@12ac982
Just created: null-----java.lang.ref.PhantomReference@1389e4
Just created: null-----java.lang.ref.PhantomReference@c20e24
Just created: null-----java.lang.ref.PhantomReference@2e7263
Just created: null-----java.lang.ref.PhantomReference@157f0dc
Just created: null-----java.lang.ref.PhantomReference@863399
Finalizing phantom 9
Finalizing phantom 8
Finalizing phantom 7
Finalizing phantom 6
Finalizing phantom 5
Finalizing phantom 4
Finalizing phantom 3
Finalizing phantom 2
Finalizing phantom 1
Finalizing phantom 0
----------------------
In queue: java.lang.ref.WeakReference@a62fc3 : null
end phanomreference
re: java try return finally Frank_Fang 2009-04-30 16:17  
刚看见一个分析,不过后面的使用javap没看懂
http://blog.csdn.net/nomad2/archive/2006/05/10/722764.aspx
共2页: 上一页 1 2