设计模式系列(一)单例模式

来源:互联网 发布:php出生以来的时间戳 编辑:程序博客网 时间:2024/05/18 13:09

前几天看了一个大神的鸡汤,对于分享这个词深有感触。所以打算把LZ所理解的设计模式写下来分享给大家。

LZ因为是17年5月份考试的软件设计师,所以提前接触到了这个,因为LZ现在也是个Java小白,所以我写的文章只能是帮助没了解过设计模式的朋友,如有错误还请各位大神在评论区指出,LZ会尽快改正。整个系列我打算一Girl这个大家都喜欢的生物来开篇,这样也比较容易让人理解。

先来简单介绍一下单例模式到底是个什么东东:单例,看名思议就是只有一个。按照前几天炒的比较火的共享女友,需求是我们需要一个类存放共享女友的储藏室,这个储藏室只能放下一个女孩。那么这个时候就可以用到单例模式了。

来一个例子:

public class Girl1 {private static Girl1 GIRL;private Girl1(){}//线程不安全的public static Girl1 getGirl() {if(GIRL==null){GIRL=new Girl1();}return GIRL;}}
上边有一个类,这个类有一个私有的包含自己本身的对象的属性和一个私有的构造方法,这个属性外界不能访问,构造方法也是私有的,这就代表着在外界我们没法访问他的属性也没办法访问他的构造方法,意思就是我们不能自己去创建一个共享女友。接着往下看,这个类提供一个公共的、静态的getGirl的方法,这个方法就像储藏室对我们提供的一个开关一样,当我们按下这个开关时,储藏室就会给我们提供一个共享女友来让我们**。

单例的思想就是这样的,但是这个还有点小瑕疵,咱们来个测试方法测试一下

                Thread t1=new Thread(new Runnable(){public void run(){for (int i = 0; i < 5; i++) {//懒汉式System.out.println("t1线程:"+Girl1.getGirl());}}});Thread t2=new Thread(new Runnable(){public void run(){for (int i = 0; i < 5; i++) {//懒汉式System.out.println("t2线程:"+Girl1.getGirl());}}});t1.start();t2.start();
大家来看一下运行结果:
t2线程:com.centling.single.Girl1@407e3dad
t1线程:com.centling.single.Girl1@10b7ce35
t2线程:com.centling.single.Girl1@10b7ce35
t1线程:com.centling.single.Girl1@10b7ce35
t2线程:com.centling.single.Girl1@10b7ce35
t1线程:com.centling.single.Girl1@10b7ce35
t2线程:com.centling.single.Girl1@10b7ce35
t1线程:com.centling.single.Girl1@10b7ce35
t2线程:com.centling.single.Girl1@10b7ce35
t1线程:com.centling.single.Girl1@10b7ce35

我们可以看到,当线程t1和线程t2第一次同时访问getGirl方法时,我们的Java没有反应过来,他认为GIRL这个共享女友是空的,所以一下就实例化了两个出来,等到t2线程再次访问的时候才是回归正轨。

既然这个是线程不安全的,那么相信学过多线程的小伙伴应该都知道了,想要线程安全这样来就行了:

public static synchronized Girl1 getGirl(){...}
上面这两种方式按照专业的方式称为懒汉式,就是说等你需要用的时候才去造这个共享女友,如果你不需要她一辈子都不会出来。

下面再来个饿汉式,就是在类被加载的时候就造出来一个等着你来用:

public class Girl2 {private static Girl2 GIRL=new Girl2();public static Girl2 getGirl() {return GIRL;}}
这里测试代码我就不写了,大家按照上面的格式自己动手试一试就明白了。

由于饿汉式是在类加载的时候就造出来可能会对储藏室的空间有影响所以又出现了一种利用内部实现类的方式

public class Girl3 {private static class Girl{private static final Girl3 GIRL=new Girl3();}private Girl3(){}public static Girl3 getGirl() {return Girl.GIRL;}}
当类被加载的时候内部类不会被加载,唯有当我们需要那个共享女友的时候他才会调用内部类的代码给我们返回一个GIRL。

以上几种用的都不是特别顺手,下面再来放个大招:

public class Girl4 {private static volatile Girl4 GIRL;private Girl4(){}public static Girl4 getGirl() {if(GIRL==null){synchronized(Girl4.class){if(GIRL==null){GIRL=new Girl4();}}}return GIRL;}}
双重锁模式:GIRL被定义为volatile,代表着他能被多个线程所看见,不管他在哪发生变化其他线程都可以看到。然后我们在得到共享女女的时候首先他就会先判断仓库里面有没有这个女女,如果没有他会锁定仓库,一次只能一个人来参观,当这一个人进来准备参观时再判断一下有没有这个女女,要是这个时候还没有的话再new出来她。这就是所谓的双重锁模式


现在走到这里单例模式介绍了个大概了,最后要说的是,单例模式,无非就是一个类只允许他有一个对象。只要符合这个定义的都可以称为单例模式,他不止上面所说的几种方式,还有很多期待大家去发掘,创造。

最后奉上源码一份供大家参考。