享元设计模式 -- 线程的不安全性

这篇博客主要分析了下享元模式的线程不安全行,因为网上没找到相关的博客,
享元模式的学习参考:
http://www.cnblogs.com/chenssy/p/3330555.html
http://www.cnblogs.com/rush/archive/2011/10/01/2197785.html

今天看了下享元的设计模式,
概念如下:

所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用。系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。

共享模式是支持大量细粒度对象的复用,所以享元模式要求能够共享的对象必须是细粒度对象。

在了解享元模式之前我们先要了解两个概念:内部状态、外部状态。

内部状态:在享元对象内部不随外界环境改变而改变的共享部分。

外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态。

由于享元模式区分了内部状态和外部状态,所以我们可以通过设置不同的外部状态使得相同的对象可以具备一些不同的特性,而内部状态设置为相同部分。在我们的程序设计过程中,我们可能会需要大量的细粒度对象来表示对象,如果这些对象除了几个参数不同外其他部分都相同,这个时候我们就可以利用享元模式来大大减少应用程序当中的对象。如何利用享元模式呢?这里我们只需要将他们少部分的不同的部分当做参数移动到类实例的外部去,然后再方法调用的时候将他们传递过来就可以了。这里也就说明了一点:内部状态存储于享元对象内部,而外部状态则应该由客户端来考虑。

可知享元模式为我们节省了很多的内存空间,省去了不必要的内存资源浪费。
在上面的定义中我们看到【内部状态】【外部状态】的定义,此处在高并发的情况应该不是线程安全的,比如,同一时间2个线程获取同一个对象,然后分别赋予不同的外部状态,这个时候就可能出现不安全的因素

抱着这个想法,我对网上的一个典型的享元模式例子做了修改,果然不出所料

代码如下:

  1. 抽象类
package com.gp;

public abstract class Flyweight {
    public abstract void Operation(int extrinsicstates);

    public abstract String getName();

    public abstract void setCount(int count);

    public abstract int getCount();
}
  1. POJO类
package com.gp;

public class ConcreteFlyweight extends Flyweight {

    @Override
    public void Operation(int extrinsicstates) {
        System.out.println("共享的Flyweight : " + extrinsicstates);
    }

    private String name = "zhangsan";
    private int count = 0;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

}
  1. 工厂类
package com.gp;

import java.util.Hashtable;

public class FlywightFactory {
    private Hashtable<String, Flyweight> flyweights = new Hashtable<String, Flyweight>();

    public FlywightFactory() {
        flyweights.put("X", new ConcreteFlyweight());
        flyweights.put("Y", new ConcreteFlyweight());
        flyweights.put("Z", new ConcreteFlyweight());
    }

    public Flyweight GetFlyweight(String key) {
        return ((Flyweight) flyweights.get(key));
    }
}
  1. 主要的类,测试类,测试线程安全的类
package com.gp;

public class FlyweightPattern {
    static FlywightFactory factory = new FlywightFactory();

    public static void main(String[] args) {
        int extrinsicstates = 1;

        Flyweight fx = factory.GetFlyweight("X");
        fx.Operation(extrinsicstates);

        new Thread() {
            public void run() {
                Flyweight fx = factory.GetFlyweight("X");
                fx.setCount(1);
                System.out.println("1:name=" + fx.getName() + ",count"
                        + fx.getCount());
            }
        }.start();
        new Thread() {
            public void run() {
                Flyweight fx = factory.GetFlyweight("X");
                fx.setCount(2);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("2:name=" + fx.getName() + ",count"
                        + fx.getCount());
            }
        }.start();
        new Thread() {
            public void run() {
                Flyweight fx = factory.GetFlyweight("X");
                fx.setCount(3);
                System.out.println("3:name=" + fx.getName() + ",count"
                        + fx.getCount());
            }
        }.start();
    }
}

输出结果

共享的Flyweight : 1
1:name=zhangsan,count1
3:name=zhangsan,count3
2:name=zhangsan,count3

此时此刻,我们发现了,第二个线程,对象信息被第三个线程改变了。

所以在使用享元模式的时候要当心线程的安全性

你可能感兴趣的:(设计模式)