设计模式:享元(FlyWeight)模式
设计模式:享元(FlyWeight)模式
一、前言
享元(FlyWeight)模式顾名思义,既是轻量级的,原因就是享元,共享元素,这里的元素指的是对象。如何共享对象,那就是在检测对象产生的时候,如果产生的是同一个对象,那么直接使用已经产生的,听起来很像是单例模式,其实享元模式的内部实现就是很类似与单例模式的懒汉模式。享元的好处就是,在某些场景下可以节省内存,从而使得程序的性能得到提升。
那么到底什么对象是可以共享的呢?!比如操作系统安装的时候就已经自动保存的图标、字体等等东西,这些东西是可以共享的,我们可以拿来直接使用,比如说word上面的字体,这些都是享元,因为不会发生改变,属于intrinsic(固有的,内在的,本质的),而有的对象是不能共享的,被称为extrinsic(外在的),本例之中自己使用TXT文档创建了几个字体,分别表示0,1,2...然后使用程序读取这些字体生成一个对象,这样的对象不能改变,因此可以用来共享。学过计算机高级结构的都知道,共享的内存一定要保证一致性,发生改变的时候也同步更新,而这里的享元从始至终都没有发生过改变,因此可以作为共享变量。
二、代码
文本文件:
BigChar类:(单个字符所表达的类)
1 package zyr.dp.flyweight; 2 3 import java.io.BufferedReader; 4 import java.io.FileNotFoundException; 5 import java.io.FileReader; 6 import java.io.IOException; 7 8 public class BigChar { 9 10 private char charname; 11 private String frontData; 12 public BigChar(char charname){ 13 this.charname=charname; 14 try { 15 BufferedReader br=new BufferedReader(new FileReader("big"+charname+".txt")); 16 StringBuffer sb=new StringBuffer(); 17 String line; 18 while((line=br.readLine())!=null){ 19 sb.append(line+"\n"); 20 } 21 br.close(); 22 frontData=sb.toString(); 23 } catch (FileNotFoundException e) { 24 e.printStackTrace(); 25 } catch (IOException e) { 26 // TODO Auto-generated catch block 27 e.printStackTrace(); 28 } 29 } 30 public void print(){ 31 System.out.println(frontData); 32 } 33 }
BigCharFactory 类:
1 package zyr.dp.flyweight; 2 3 import java.util.HashMap; 4 5 public class BigCharFactory { 6 7 private HashMap pool=new HashMap(); 8 9 private static BigCharFactory bigCharFactory=new BigCharFactory(); 10 11 private BigCharFactory(){ 12 13 } 14 15 public static BigCharFactory getInstance(){ 16 return bigCharFactory; 17 } 18 19 public synchronized BigChar getBigChar(char name){ 20 BigChar bigchar=(BigChar)pool.get(""+name); 21 if(bigchar==null){ 22 bigchar=new BigChar(name); 23 pool.put(""+name, bigchar); 24 } 25 return bigchar; 26 } 27 public BigChar getBigCharNotUsed(char name){ 28 return new BigChar(name); 29 } 30 31 }
BigString类:
1 package zyr.dp.flyweight; 2 3 public class BigString { 4 5 private BigChar [] bigchars; 6 public BigString(String word,boolean isUsed){ 7 if(isUsed == true){ 8 bigchars=new BigChar[word.length()]; 9 BigCharFactory bf=BigCharFactory.getInstance(); 10 for(int i=0;i<word.length();i++){ 11 bigchars[i]=bf.getBigChar(word.charAt(i)); 12 } 13 }else{ 14 bigchars=new BigChar[word.length()]; 15 BigCharFactory bf=BigCharFactory.getInstance(); 16 for(int i=0;i<word.length();i++){ 17 bigchars[i]=bf.getBigCharNotUsed(word.charAt(i)); 18 } 19 } 20 } 21 22 public void print(){ 23 for(int i=0;i<bigchars.length;i++){ 24 bigchars[i].print(); 25 } 26 } 27 }
Main类:
1 package zyr.dp.flyweight; 2 3 public class Main { 4 5 public static void main(String[] args) { 6 String name="221100"; 7 testMemory( name, false); 8 testMemory( name, true); 9 } 10 public static void testMemory(String name,boolean isUsed){ 11 System.out.println("是否使用轻量级:"+isUsed); 12 BigString bs=new BigString(name,isUsed); 13 bs.print(); 14 countMemory(); 15 System.out.println("================="); 16 } 17 public static void countMemory(){ 18 Runtime.getRuntime().gc(); 19 System.out.println("已使用内存:"+(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory())); 20 } 21 }
运行结果:
是否使用轻量级:false ---****------ -------*----- --------*---- -----**------ ----*-------- --*******---- ---****------ -------*----- --------*---- -----**------ ----*-------- --*******---- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- ----*****---- ---*-----*--- --*-------*-- --*-------*-- ---*-----*--- ----*****---- ----*****---- ---*-----*--- --*-------*-- --*-------*-- ---*-----*--- ----*****---- 已使用内存:879440 ================= 是否使用轻量级:true ---****------ -------*----- --------*---- -----**------ ----*-------- --*******---- ---****------ -------*----- --------*---- -----**------ ----*-------- --*******---- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- -----**----- ----*****---- ---*-----*--- --*-------*-- --*-------*-- ---*-----*--- ----*****---- ----*****---- ---*-----*--- --*-------*-- --*-------*-- ---*-----*--- ----*****---- 已使用内存:876928 =================
三、总结
在我们的程序中,使用了单例模式,同时为了享元,我们使用了类似于单例模式中的懒汉模式,加入synchronized是为了防止多线程中出现误入,当然在本例中是没有多线程的,加不加锁无所谓。同时,我们对比了没有使用享元的例子,(对比之前先启动GC回收一次内存)可以发现所占用的内存空间,明显使用了享元的占用的内存小,而没有使用享元的占用的内存多。并且这里我们要注意垃圾回收机制,在工厂类中,使用了HashMap来将BigChar对象保存起来,这样就形成了一个DAC(有向无环图),只要pool变量不被释放,我们使用的共享单元是不会被释放的。这样就保证了BigChar对象数组不被释放,在使用享元模式的时候一定要特别注意这种情况,因为垃圾回收器(GC)在内存占用过多的时候被唤醒,然后清理那些被再被使用的内存,采用的方式就是DAC。