`
pan_java
  • 浏览: 287906 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

多线程设计模式 -- immutable

 
阅读更多
对象
      //类定义为 final
      public final class Person {
	//类变量定义为 final
         private final String name;
	private final String address;

	public Person(String name, String address) {
		this.name = name;
		this.address = address;
	}

	public String getName() {
		return name;
	}

	public String getAddress() {
		return address;
	}

         //name ,address  是不能修改的,所在多线程下安全的
	public String toString() {
		return "[ Person: name = " + name + ", address = " + address + " ]";
	}
}



线程
public class PrintPersonThread extends Thread {
	private Person person;

	public PrintPersonThread(Person person) {
		this.person = person;
	}

	public void run() {
		while (true) {
		    //将调用Person 的toString 方法 
                          //当多线程情况Person的name,address是final的 不能被修改所以是线程安全
                      System.out.println(Thread.currentThread().getName() + " prints "
					+ person);
		}
	}
}



public class Main {
    public static void main(String[] args) {
        Person alice = new Person("Alice", "Alaska");
        new PrintPersonThread(alice).start();
        new PrintPersonThread(alice ).start();
        new PrintPersonThread(alice ).start();
    }
}


用final标示比使用锁块和syschronized效率要好
同时 Integer ,等一些基本类型的包装类 都是使用immutable 模式

分享到:
评论
18 楼 swen00 2009-07-09  
我承认,我没看懂...
17 楼 pan_java 2009-07-09  
liangguanhui 写道
运动设计模式 - 四轮

四轮总比两轮和三轮快。


google 了一下没有找到相关资料能提供一下吗?
16 楼 蓝月鸟 2009-07-09  
liangguanhui 写道
运动设计模式 - 四轮

四轮总比两轮和三轮快。


生物运动设计模式 - 蜈蚣

在某些腿脚受损情况下依然正常行走
15 楼 liangguanhui 2009-07-09  
运动设计模式 - 四轮

四轮总比两轮和三轮快。
14 楼 ivyloo 2009-07-09  
乍一看,有些道理,
可是仔细一想,lz的大方向就错了,
因为你先设计`不变模式`,后用到多线程中的。
但是在实际中是现有现实需求,而在需求中你就不能保证:类是`不变模式`的了!

所以,我认为你是先设计不变类,然后硬套到多线程编程中,但是没有实际意义,因为在实际中不能保证这一点。
13 楼 holan 2009-07-09  
我只想说我晕~~~
12 楼 pan_java 2009-07-09  
whaosoft 写道
zhengban 写道
这个例子不能说明多线程对数据的操控。
有空去看看多线程下的数据结构,现在这块还不是成熟技术。

可能是我技术有限 我也没看出 多线程对数据的操控

这是一种模式,如果你在多线程要利用一个共享数据对象作相关断.而这个数据对象实例化后又不需要改变.就可以用这个模式.因为这个数据对象是绝对安全的.多线程下不会去修改这个数据对象.
我们同步一个方法同时只让一个线程访问的主要作用也就是不能多个线程同时修改类变量.
而这个数据对象的类变量全部都是final 实例化后就无法修改了.所有就不用同步相关的方法.提交执行的效率.

如果你在删除这个共享数据类变量的final加上相关set方法.这个实例就可以修改了,同时在多线程也是不安全的了.如果方法不是同步的.同时两个线程访问这个方法.同时两个线程式都修改这个共享数据的变量值,那么是不是不安全的.所以要同步这个方法.

jdk 中 Integer ,Boolean 等一些包装类都是利用这个模式,你们可以去看看源代码.


package java.lang;
public [color=red]final[/color] class Boolean implements java.io.Serializable,
                                      Comparable<Boolean>
{
    
    public static final Boolean TRUE = new Boolean(true);

    public static final Boolean FALSE = new Boolean(false);

    public static final Class<Boolean> TYPE = Class.getPrimitiveClass("boolean");

    private [color=red]final[/color] boolean value;

    private static final long serialVersionUID = -3665804199014368530L;

    
    public Boolean(boolean value) {
	this.value = value;
    }

       public Boolean(String s) {
	this(toBoolean(s));
    }

      public static boolean parseBoolean(String s) {
        return toBoolean(s);
    }

       [color=red]public boolean booleanValue() {
	return value;
    }[/color]

        public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

        public static Boolean valueOf(String s) {
	return toBoolean(s) ? TRUE : FALSE;
    }

   
    public static String toString(boolean b) {
        return b ? "true" : "false";
    }

       public String toString() {
	return value ? "true" : "false";
    }

        public int hashCode() {
	return value ? 1231 : 1237;
    }

       public boolean equals(Object obj) {
	if (obj instanceof Boolean) {
	    return value == ((Boolean)obj).booleanValue();
	} 
	return false;
    }
    public static boolean getBoolean(String name) {
        boolean result = false;
        try {
            result = toBoolean(System.getProperty(name));
        } catch (IllegalArgumentException e) {
        } catch (NullPointerException e) {
        }
        return result;
    }

       public int compareTo(Boolean b) {
        return (b.value == value ? 0 : (value ? 1 : -1));
    }

    private static boolean toBoolean(String name) { 
	return ((name != null) && name.equalsIgnoreCase("true"));
    }
}
11 楼 whaosoft 2009-07-09  
zhengban 写道
这个例子不能说明多线程对数据的操控。
有空去看看多线程下的数据结构,现在这块还不是成熟技术。

可能是我技术有限 我也没看出 多线程对数据的操控
10 楼 zhengban 2009-07-09  
这个例子不能说明多线程对数据的操控。
有空去看看多线程下的数据结构,现在这块还不是成熟技术。
9 楼 kobevaliant 2009-07-09  
运动设计模式 - 三轮

三轮车比支着车踢子的自行车在运动中稳定性要好

8 楼 jiyanliang 2009-07-09  
说明转载哦
http://dev.csdn.net/author/axman/6f0a40330ab349b7915a8247c0f9c3cc.html

另:他这里说的不变模式的定义也是一家之言吧。。。
看看java与模式中关于不变模式的定义:不变模式只涉及到一个类,一个类的内部创建之后,在整个生命周期都不会发生变化时,这样的类就是不变类,这种使用不变类的做法就是不变模式。当然他说的强不变模式就是你这里第一个帖子中涉及的person类。
pan_java 写道
star022 写道
pan_java 写道
1.当实例产生后,不再改变时
2.实例需要共享,而且访问很频繁时


典型的静态资源,连锁都不需要,这样的资源与多线程几乎没什么关系!


表面上看这个模式的确是这样的.
Person这个类生成实例后是无法更改的.因为没有提供相关Set方法.类变量又是 private final 的.类也是 final 不会产生子类.
所以在多程线下这个这个类绝对安全.
假设我在Person类中删除类变量 final 标示,然后加上类变量了相关Set 方法,那么你说在多线程这个类还是安全的吗.答案肯定是不安全的.因为我们可以利用set 方法改变类变量的属性.

如果你有一个共享的实例,实例化后又不需要改变他了.那么设置类变量是final让他们不能修改.所以这样方法也就不用同步等待,提高执行效率.

包括我本人对这个例子也有一点疑惑.所以找了一个有说服的文章,如下:

。。。。。。。。。。。。

7 楼 repsihWDX 2009-07-09  
呵呵 你没理解为什么这里用final关键字的。lz.主要是可见性问题,和有没有setter无关的
6 楼 pan_java 2009-07-08  
star022 写道
pan_java 写道
1.当实例产生后,不再改变时
2.实例需要共享,而且访问很频繁时


典型的静态资源,连锁都不需要,这样的资源与多线程几乎没什么关系!


表面上看这个模式的确是这样的.
Person这个类生成实例后是无法更改的.因为没有提供相关Set方法.类变量又是 private final 的.类也是 final 不会产生子类.
所以在多程线下这个这个类绝对安全.
假设我在Person类中删除类变量 final 标示,然后加上类变量了相关Set 方法,那么你说在多线程这个类还是安全的吗.答案肯定是不安全的.因为我们可以利用set 方法改变类变量的属性.

如果你有一个共享的实例,实例化后又不需要改变他了.那么设置类变量是final让他们不能修改.所以这样方法也就不用同步等待,提高执行效率.

包括我本人对这个例子也有一点疑惑.所以找了一个有说服的文章,如下:


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

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

还是先看具体的例子:

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

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

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

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

1.不变类设计起来有一定难度.对于上面这个用来示例的Dog,由于其本身的属性,方法都很简单,我们还可以充分地考虑到可
以改变它状态的各种情况.但对于复杂的类,要保证它的不变性,是一个非常吃力的工作.

不变类中,任何一个必要都件都不是充要条件,虽然连老骨灰都没有这么说过,但我还是要真诚地目光深邃语气凝重地告诉你.
没有任何条件是充要条件的意思就是如果任何一个必要条件你没考虑到,那它就会无法保证类的不可变性.没有规范,没有模
板,完全看一人设计人员的经验和水平.也许你自以为考虑很全面的一个"不变类"在其他高手面前轻而易举地就"可变"了.

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

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

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

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

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

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

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

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


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


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

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

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

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

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

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

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

        for(Object o: tmp){
            bp.checkGuard(o);
        }
        for(Object o: tmp){
            o.xxx;
        }
    }
5 楼 star022 2009-07-08  
pan_java 写道
1.当实例产生后,不再改变时
2.实例需要共享,而且访问很频繁时


典型的静态资源,连锁都不需要,这样的资源与多线程几乎没什么关系!
4 楼 pan_java 2009-07-08  
1.当实例产生后,不再改变时
2.实例需要共享,而且访问很频繁时
3 楼 star022 2009-07-08  
pan_java 写道
liufeng820 写道
你写的东西和多线程有什么关系?

在多线程下保护共享资源(Person 对象)不被修改.


这个Person资源只能被访问,而不能被修改,在多线程环境下意义不大!
2 楼 pan_java 2009-07-08  
liufeng820 写道
你写的东西和多线程有什么关系?

在多线程下保护共享资源(Person 对象)不被修改.
1 楼 liufeng820 2009-07-08  
你写的东西和多线程有什么关系?

相关推荐

    java多线程设计模式详解(PDF及源码)

    书中包含Java线程的介绍导读、12个重要的线程设计模式和全书总结以及丰富的附录内容。第一章相关线程设计模式的介绍,都举一反三使读者学习更有效。最后附上练习问题,让读者可以温故而知新,能快速地吸收书中的...

    java多线程设计模式 (PDF中文版, 附源码)

    目录: 漫谈UML Introduction 1 Java语言的线程 Introduction 2 多线程...总结 多线程程序设计的模式语言 附录A 练习问题的解答 附录B Java的内存模型 附录C Java线程的优先级 附录D 线程相关的主要API 附录E 参考文献

    java8源码-GraphicMultiThreadDesignPattern:图解Java多线程设计模式

    记录自己学习《图解Java多线程设计模式》这本书的全部过程 本书上传的所有代码都是可以运行的,在此附上本书源码地址: 在此向本书作者和译者表示感谢 运行环境 Eclipse版本:Oxygen.2 Release (4.7.2) JDK版本:jdk...

    21-C-ImmutableObject-Code.zip

    在编程领域,不可变对象(Immutable Object)是一种重要的设计模式,尤其在多线程和并发环境中,它提供了安全性和性能优化。在这个初学者教程中,我们将深入探讨C#中的不可变对象,包括“内部不变性”和“观察不变性...

    汪文君高并发编程实战视频资源下载.txt

    │ 高并发编程第二阶段41讲、多线程设计模式内容回顾与总结.mp4 │ 高并发编程第二阶段42讲、ClassLoader课程大纲介绍.mp4 │ 高并发编程第二阶段43讲、类加载的过程以及类主动使用的六种情况详细介绍.mp4 │ 高...

    汪文君高并发编程实战视频资源全集

    │ 高并发编程第二阶段41讲、多线程设计模式内容回顾与总结.mp4 │ 高并发编程第二阶段42讲、ClassLoader课程大纲介绍.mp4 │ 高并发编程第二阶段43讲、类加载的过程以及类主动使用的六种情况详细介绍.mp4 │ 高...

    82丨开源实战三(中):剖析GoogleGuava中用到的几种设计模式1

    不可变对象一旦创建,其状态就不能改变,这为多线程环境提供了天然的安全性,并且可以被安全地共享。Guava的Immutable类通过严格的内部一致性检查,确保对象一旦构造完成,就不会有修改的可能,同时也优化了性能,...

    高效安全的Immutable List好处.docx

    这种设计模式在多线程环境和安全性需求较高的场景下非常有用,因为它天然具备线程安全性和防止意外修改的特性。 线程安全:由于 Immutable List 不可变,所以在多线程环境中,多个线程可以共享同一个实例而无需担心...

    immutable-object:如何构建和使用不可变对象的示例

    这种设计模式在Java等编程语言中广泛应用于提高并发性能、保证数据安全以及简化编程逻辑。本篇文章将深入探讨如何在Java中构建和使用不可变对象,通过实例来阐述其原理和优势。 首先,理解不可变对象的基本特征: 1...

    java concurrency in practice.rar

    4. **并发设计模式** - **生产者消费者模式**:通过阻塞队列实现线程间的协作。 - **工作窃取**:避免线程饥饿,如Fork/Join框架中的Work Stealing。 - **双端队列(Bounded Buffer)**:使用ArrayBlockingQueue...

    Effective Objective-C 2.0

    10. 设计模式:Objective-C开发者在设计应用程序时,经常会用到设计模式。书中会介绍如何运用这些模式,例如单例模式、工厂模式、代理模式和观察者模式等,来优化代码结构和复用代码。 11. 与Cocoa框架的交互:...

    java 最新面试题库 含N家公司 面试

    - **不可变对象**(Immutable Objects):创建不可变对象可以确保其在多线程环境中的安全性,因为一旦对象被创建后就不能被改变。 #### 2. 内存模型与垃圾回收 这部分可能是指Java内存模型及垃圾回收机制。 - **...

    JavaConcurrencyPattern:Java并发模式

    这些文章已扩充为一本书:《Java多线程编程实战指南(设计模式篇)》,由电子工业出版社出版,当当、亚马逊、京东、互动出版网、淘宝等各大书店有售。 【样章】 Active Object模式: Immutable Object模式: Two-...

    《Java Concurrency in Practice》代码示例

    这本书深入浅出地探讨了Java平台上的多线程和并发编程,提供了丰富的实践经验和设计模式,帮助开发者编写高效、可靠和可维护的并发程序。 在提供的资源中,我们有两个源码jar文件:jcip-examples-src.jar和jcip-...

    Java Concurrency in Practice

    4. **并发设计模式**:介绍了如双重检查锁定、发布与初始化的安全、避免活跃性问题(死锁、活锁、饥饿)等常见的并发设计模式,指导开发者写出高效且可靠的并发代码。 5. **并发集合**:Java并发包中的并发集合(如...

    JAVA程序员面试题收集

    - **MVC模式详解**:深入探讨Model-View-Controller的设计模式,以及其在J2EE中的应用。 - **Struts框架介绍**:简要介绍Struts框架的核心组件和使用方法。 ### 八、其他高级主题 #### EJB生命周期 - **EJB生命...

    Java并发安全控制(培训)

    - **单例模式**:单例对象在多线程环境中只允许有一个实例,因此需要保证线程安全。 2. **检查再运行**:即“Check-Then-Act”模式,先检查条件是否满足,然后根据结果执行相应操作。这种模式通常需要与锁配合使用...

    提高C#编程50要点

    - 更灵活的设计模式。 - 支持多重继承特性。 #### 20. 正确使用 override - **概念**:`override`关键字用于覆盖基类中的虚方法。 - **注意事项**: - 必须存在于派生类中。 - 方法签名必须完全匹配。 #### 21...

    guava-26.0-jre.zip

    4. **并发工具**:Guava提供了丰富的并发工具类,如 ListeningExecutorService、CountDownLatch、CyclicBarrier 和 Exchanger,它们简化了多线程编程,提高了并发程序的可靠性。 5. **字符串处理**:Guava提供了...

Global site tag (gtag.js) - Google Analytics