linux社区爱心援助Linux认证系列教程业界动态站务新闻公司招聘建议留言网址大全LPI专题CISCO专题
设为首页
加入收藏
管理团队
JSP  
JAVA  
PERL  
 您的位置:首页 > article > Linux开发区 > 软件开发 >
栏目导栏
资料搜索
热门文章
·Linux 下 C 语言编程
·Linux下的通用线程池创建
·C++字符串转换篇
·linux C 进程操作篇
·linux上的C/C++编译器gcc/egcs
·linux C 文件权限控制篇
·GCC使用手册
·linux C 接口处理篇
·在Redhat Linux上安装 GCC 编译
·GCC使用指南
·C语言运算符
·Linux下C开发环境的构成和安装
·fopen()函数的参数说明
·GCC使用手册与常用命令
·Scheme 语言介绍
最新文章
·在Ubuntu Linux 8.04上构建GCC
·Linux操作系统下Socket编程地址
·将VC程序移植到Linux系统的几点
·Linux下malloc/free与new/dele
·Linux下用GTK和socket实现简单
·Linux操作系统下让Tomcat启动在
·Linux操作系统中如何编译C程序
·几种常被人们忽略的Linux系统下
·Eclipse编程工具 在Ubuntu下的
·Linux操作系统下的网络地址转换
·老手经验谈:Linux驱动程序开发
·Linux操作系统多线程同步Mutex
·Linux操作系统下C程序语言简易
·Linux系统平台下关于GCC编译及
·解决Linux系统下管道被接受方关
Google
 
多线程编程 实战篇秘籍 (二)
[ 作者:  加入时间:2006-11-14 11:20:59  来自:Linux联盟收集 ]

多线程编程的设计模式 不变模式(二)

 不变模式(Immutable Pattern)顾名思义,它的状态在它的生命周期内是永恒的(晕,永恒的日月星晨,对象如人,6pULinux联盟
太渺小,谈不上永恒!
),不会改变的.对于其中的不变类(Immutable Class),它的实例可以在运行期间保持状态永远不会被6pULinux联盟
改变,所以不需要采取共享互斥机制来保护,如果运用得当可以节省大量的时间成本.

 请注意上面这段话,不变模式其中的不变类,说明不变类只是不变模式中一个组成部分,不变类和与之相辅的可变6pULinux联盟
类,以及它们之间的关系才共同构成不变模式!所以在涉及不变模式的时候一定要研究一个类是不变的还是可变的(Mutable).6pULinux联盟
在jdk中的String类和StringBuffer类就组成了一个不变模式.

还是先看具体的例子:

final class Dog{6pULinux联盟
    private final String name;6pULinux联盟
    private final int age;6pULinux联盟
    public Dog(String name,int age){6pULinux联盟
        this.name = name;6pULinux联盟
        this.age = age;6pULinux联盟
    }6pULinux联盟
    6pULinux联盟
    public String getName(){return this.name;}6pULinux联盟
    public int getAge(){return this.age;}6pULinux联盟
    6pULinux联盟
    public String toString(){6pULinux联盟
        return "Dog's name = " + this.name + ",age = " + this.age;6pULinux联盟
    }6pULinux联盟
}

1.Dog类本身被声明为final,可以保证它本身的状态不会被子类扩展方法所改变.6pULinux联盟
2.Dog类的所有成员变量都是final的,保证它在构造后不会被重新赋值.而且Dog类所有属性是private的,只提供getter访问.6pULinux联盟
3.Dog类的能传入的参数本身是Immutable的.这一点非常重要将在下面具体说明.6pULinux联盟
以上条件都是必要条件,而不是充要条件.

class DisplayDog extends Thread{6pULinux联盟
    private Dog dog;6pULinux联盟
    public DisplayDog(Dog dog){6pULinux联盟
        this.dog = dog;6pULinux联盟
    }6pULinux联盟
    6pULinux联盟
    public void run(){6pULinux联盟
        while(true){6pULinux联盟
            System.out.println(this.getName() + " display: " + dog);6pULinux联盟
        }6pULinux联盟
    }6pULinux联盟
}

DisplayDog类是把一个Dog对象传入后,不断显示这个dog的属性.我们会同时用多个线程来显示同一dog对象,看看它们在共享6pULinux联盟
同一对象时对象的状态:6pULinux联盟
public class Test {6pULinux联盟
    public static void main(String[] args) throws Exception {6pULinux联盟
        Dog dog = new Dog("Sager",100);6pULinux联盟
        new DisplayDog(dog).start();6pULinux联盟
        new DisplayDog(dog).start();6pULinux联盟
        new DisplayDog(dog).start();6pULinux联盟
    }6pULinux联盟
}6pULinux联盟
运行这个例子你可以等上一个月,虽然运行一年都正常并不能说明第366天不出现异常,但我们可以把这样的结果认为是一种6pULinux联盟
说明.多个线程共享一个不变类的实例时,这个实例的状态不会发生改变.事实上它没有地方让你去改变.6pULinux联盟
在临界区模式中有些操作必须只允许有一个线程操作,而这个类本身以及对它的访问类中并不需要进行临界区保护,这就让多6pULinux联盟
个线程不必等待从而提高了性能.

既然有这么好的优势,那我们在需要临界区保护的对象为什么不都设计成不变类呢?

1.不变类设计起来有一定难度.对于上面这个用来示例的Dog,由于其本身的属性,方法都很简单,我们还可以充分地考虑到可6pULinux联盟
以改变它状态的各种情况.但对于复杂的类,要保证它的不变性,是一个非常吃力的工作.6pULinux联盟
 6pULinux联盟
不变类中,任何一个必要都件都不是充要条件,虽然连老骨灰都没有这么说过,但我还是要真诚地目光深邃语气凝重地告诉你.6pULinux联盟
没有任何条件是充要条件的意思就是如果任何一个必要条件你没考虑到,那它就会无法保证类的不可变性.没有规范,没有模6pULinux联盟
板,完全看一人设计人员的经验和水平.也许你自以为考虑很全面的一个"不变类"在其他高手面前轻而易举地就"可变"了.

2.不变类的种种必要条件限制了类设计的全面性,灵活性.这点不用多说,简单说因为是不变类,所以你不能A,因为是不变类,你6pULinux联盟
不能B.

当然,如果你是一人很有经验的设计者,你能成功地设计一个不变类,但因为它的限制而失去一些功能,你就要以使用与之相辅6pULinux联盟
的可变类.并且它们之间可以相互转换,在需要不变性操作的时候以不变类提供给用户,在需要可变性操作的时候以可变类提供6pULinux联盟
给用户.

在jdk中String被设计为不可变类,一旦生成一个String对象,它的所有属性就不会被变,任何方法要么返回这个对象本身的原6pULinux联盟
始状态,要么抛弃原来的字符串返回一个新字符串,而绝对不会返回被修改了的字符串对象.6pULinux联盟
但是很多时候返回新字符串抛弃原来的字符串对象这样的操作太浪费资源了.特别是在循环地操作的时候:

 String s = "Axman";6pULinux联盟
 for(int i=0;i<1000*1000;i++) s += "x";这样的操作是致命的.6pULinux联盟
那么这种时候需要将原始的不变的s包装成可变的StringBuffer来操作,性能的改变可能是成千上万倍:

        StringBuffer sb = new StringBuffer(s); //将不变的String包装成可变的String;6pULinux联盟
        for(int i=0;i<1000*1000;i++)6pULinux联盟
            sb.append("x");6pULinux联盟
        s = new String(sb); //将可变类封装成不变类.虽然可以调用toString(),但那不是可变到不变的转换.

在将可变类封装到不变类的时候要特别小心.因为你传入的引用在外面是可以被修改的.所以即使你不变类本身不能去改变属6pULinux联盟
性但属性有一个外部引用.可以在外面修改:

final class MutableDog{6pULinux联盟
    private String name;6pULinux联盟
    private int age;6pULinux联盟
    public MutableDog(String name,int age){6pULinux联盟
        this.name = name;6pULinux联盟
        this.age = age;6pULinux联盟
    }6pULinux联盟
    public synchronized void setDog(String name,int age){6pULinux联盟
        this.name = name;6pULinux联盟
        this.age = age;6pULinux联盟
    }6pULinux联盟
    public String getName(){return this.name;}6pULinux联盟
    public int getAge(){return this.age;}

    public synchronized String toString(){6pULinux联盟
        return "Dog's name = " + this.name + ",age = " + this.age;6pULinux联盟
    }6pULinux联盟
    6pULinux联盟
     public synchronized ImmatableDog getImmatableDog(){6pULinux联盟
         return new ImmatableDog(this);6pULinux联盟
     }6pULinux联盟
}

final class ImmatableDog{6pULinux联盟
    private final String name;6pULinux联盟
    private final int age;6pULinux联盟
    public ImmatableDog(String name,int age){6pULinux联盟
        this.name = name;6pULinux联盟
        this.age = age;6pULinux联盟
    }6pULinux联盟
    6pULinux联盟
    public ImmatableDog(MutableDog dog){6pULinux联盟
        this.name = dog.getName();6pULinux联盟
        this.age = dog.getAge();6pULinux联盟
    }    6pULinux联盟
    6pULinux联盟
    public String getName(){return this.name;}6pULinux联盟
    public int getAge(){return this.age;}6pULinux联盟
    6pULinux联盟
    public String toString(){6pULinux联盟
        return "Dog's name = " + this.name + ",age = " + this.age;6pULinux联盟
    }6pULinux联盟
}

6pULinux联盟
MutableDog类是可变的,可以满足我们利用对象的缓冲来让对象成为表示另一个实体的功能.但它们之间6pULinux联盟
随时可以根据需要相互转换,但是我们发现:6pULinux联盟
    public ImmatableDog(MutableDog dog){6pULinux联盟
        this.name = dog.getName();6pULinux联盟
        this.age = dog.getAge();6pULinux联盟
    }6pULinux联盟
这个方法是不安全的.当一个属性为"Sager",100的dog被传进来后,执行this.name = dog.getName();后,6pULinux联盟
如果线程切换到其它线程执行,那么dog的属性就可能是"p4",80,这时再执行this.age = dog.getAge();6pULinux联盟
我们就会得到一个属性为"Sager",80的这样一个错误的不可变对象.这是一个非常危险的陷井.在这里我们6pULinux联盟
可以通过同上来解决:6pULinux联盟
    public ImmatableDog(MutableDog dog){6pULinux联盟
        synchronized(dog){6pULinux联盟
            this.name = dog.getName();6pULinux联盟
            this.age = dog.getAge();6pULinux联盟
        }6pULinux联盟
    }6pULinux联盟
注意这里同步的MutableDog,它将会和MutableDog的setDog产生互斥.它们都需要获取同一MutableDog对象的6pULinux联盟
锁,如果MutableDog的setDog不是方法同步(synchronized(this)),即使ImmatableDog(MutableDog dog)中同步6pULinux联盟
了dog,也不能保证安全,它们需要在同一对象上互斥.

但同步也并不一定能保证传入的参数不可变:

我曾以下面这个例子来作为对一个Java程序员的终极测试,终极测试的意思是说,如果你不懂并不说明你水平6pULinux联盟
差,但如何你懂这个问题那就没有必要测试其它问题了.

    public static void test(Object[] objs){6pULinux联盟
        java.security.BasicPermission bp  =  xxxxx;

        for(Object o: objs){6pULinux联盟
            bp.checkGuard(o);6pULinux联盟
        }6pULinux联盟
        for(Object o: abjs){6pULinux联盟
            o.xxx;6pULinux联盟
        }6pULinux联盟
    }6pULinux联盟
当一个数据被传入后我们需要对其中的每个元素做安全性检查,如果通不过bp.checkGuard(o);自己会抛出6pULinux联盟
异常的.但如果objs[0]被bp.checkGuard(o);过后,外面的线程通过objs去修改objs[0],这时就会把一个没有6pULinux联盟
经过安全检查的对象绕过bp.checkGuard(o);而直接被调用.假如Runtime.exec(String[] args)就是这样实6pULinux联盟
现我们可以想象会出现什么问题.

所以对于这样的传入参数,我们可以将其在方法类复制为本地变量(数组).或使用它的深度clone,打断与方法6pULinux联盟
外的联系:

    public static void test(Object[] objs){6pULinux联盟
 Object tmp = new Object[objs.lenth];6pULinux联盟
 System.arrayCopy(objs,tmp,0,0,objs.lenth);6pULinux联盟
 java.security.BasicPermission bp  =  xxxxx;

        for(Object o: tmp){6pULinux联盟
            bp.checkGuard(o);6pULinux联盟
        }6pULinux联盟
        for(Object o: tmp){6pULinux联盟
            o.xxx;6pULinux联盟
        }6pULinux联盟
    }

6pULinux联盟
先说到这里吧.休息一下.

Linux联盟收集整理 ,转贴请标明原始链接,如有任何疑问欢迎来本站Linux论坛讨论
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
 ·多线程编程 深入理解DCL的安全性  (2006-11-13 10:51:41)
 ·多线程编程的设计模式 不变模式(二)  (2006-11-13 10:47:51)
 ·多线程编程的设计模式 临界区模式(二)  (2006-11-13 10:46:15)
 ·Solaris2.4 多线程编程指南7--编程指南  (2006-07-11 19:49:05)
 ·Solaris2.4 多线程编程指南6--编译和调试  (2006-07-11 19:48:21)
 ·Solaris2.4 多线程编程指南5--安全和不安全的接口函数  (2006-07-11 19:47:46)
 ·Solaris2.4 多线程编程指南4--操作系统编程  (2006-07-11 19:47:09)
 ·Solaris2.4 多线程编程指南3--使用同步对象编程  (2006-07-11 19:46:25)
 ·Solaris2.4 多线程编程指南2--用多线程编程  (2006-07-11 19:44:46)
 ·Solaris2.4 多线程编程指南1--线程基础  (2006-07-11 19:44:05)