Java常用的设计模式03:常用设计模式之单例模式

1.java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
  单例模式有一下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

  正是由于这个特点,单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或 文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境 下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。

2.Java单例模式3种写法:

(1)懒汉:

 1 public class Singleton {
 2     private static Singleton instance;
 3     private Singleton (){}
 4     public static Singleton getInstance() {
 5     if (instance == null) {
 6         instance = new Singleton();
 7     }
 8     return instance;
 9     }
10 }
11 

致命的是在多线程不能正常工作,线程不安全。

以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,对getInstance这个方法改造有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全:

优化懒汉,实现线程安全。做法如下3种:

        •在getInstance方法上加同步:     

public static synchronized Singleton getInstance() {
         if (single == null) {
             single = new Singleton();
         }
        return single;
}

•双重检查锁定:

public static synchronized Singleton getInstance() {
         if (single == null) {
             single = new Singleton();
         }
        return single;
}

   •静态内部类:

public class Singleton {
    private static class LazyHolder {
       private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
       return LazyHolder.INSTANCE;
    }
}    

(2)饿汉:

//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
    private Singleton1() {}
    private static final Singleton1 single = new Singleton1();
    //静态工厂方法
    public static Singleton1 getInstance() {
        return single;
    }
}  

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

其实饿汉还有变种编写方式如下:

 1 public class Singleton1 {
 2     private Singleton1 instance = null;
 3     static {
 4     instance = new Singleton1();
 5     }
 6     private Singleton1 (){}
 7     public static Singleton1 getInstance() {
 8     return this.instance;
 9     }
10 }
11 

表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。

(3)登记式单例(可忽略)

//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
    private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
    static{
        Singleton3 single = new Singleton3();
        map.put(single.getClass().getName(), single);
    }
    //保护的默认构造子
    protected Singleton3(){}
    //静态工厂方法,返还此类惟一的实例
    public static Singleton3 getInstance(String name) {
        if(name == null) {
            name = Singleton3.class.getName();
            System.out.println("name == null"+"--->name="+name);
        }
        if(map.get(name) == null) {
            try {
                map.put(name, (Singleton3) Class.forName(name).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return map.get(name);
    }
    //一个示意性的商业方法
    public String about() {
        return "Hello, I am RegSingleton.";
    }
    public static void main(String[] args) {
        Singleton3 single3 = Singleton3.getInstance(null);
        System.out.println(single3.about());
    }
}  

       

        登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。 这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

(4)枚举实现单例模式:

        单例模式约束一个类只能实例化一个对象。在Java中,为了强制只实例化一个对象,最好的方法是使用一个枚举量。这个优秀的思想直接源于Joshua Bloch的《Effective Java》(《Java高效编程指南》)。如果你的藏书室里还没有这本书,请搞一本,它是迄今为止最优秀的Java书籍之一。

这里有几个原因关于为什么在Java中宁愿使用一个枚举量来实现单例模式:

       ♦ 自由序列化;

        保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);

       ♦ 线程安全;

案例:

 1 public enum AnimalHelperSingleton {
 2
 3     INSTANCE;
 4
 5     private AnimalHelperSingleton(){
 6
 7     }
 8
 9     public Animal[] buildAnimalList(){
10         final Animal[] animals = new Animal[10];
11
12         animals[0] = new SimpleAnimal(Animal.AnimalClass.MAMMAL,
13                 "Dog", true, Color.GRAY);
14         animals[1] = new SimpleAnimal(Animal.AnimalClass.MAMMAL,
15                 "Cat", true, Color.YELLOW);
16         animals[2] = new SimpleAnimal(Animal.AnimalClass.AMPHIBIAN,
17                 "Frog", true, Color.GREEN);
18         animals[3] = new SimpleAnimal(Animal.AnimalClass.BIRD,
19                 "Crow", true, Color.BLACK);
20         animals[4] = new SimpleAnimal(Animal.AnimalClass.BIRD,
21                 "Cardinal", true, Color.RED);
22         animals[5] = new SimpleAnimal(Animal.AnimalClass.ARTHROPOD,
23                 "Mantis", false, Color.GREEN);
24         animals[6] = new SimpleAnimal(Animal.AnimalClass.ARTHROPOD,
25                 "Spider", false, Color.ORANGE);
26         animals[7] = new SimpleAnimal(Animal.AnimalClass.MAMMAL,
27                 "Tiger", true, Color.ORANGE);
28         animals[8] = new SimpleAnimal(Animal.AnimalClass.MAMMAL,
29                 "Bear", true, Color.BLACK);
30         animals[9] = new SimpleAnimal(Animal.AnimalClass.BIRD,
31                 "Owl", true, Color.BLACK);
32
33         return animals;
34     }
35
36 }

如何使用:

//Call singleton to build the animal list.
Animal[] animals = AnimalHelperSingleton.INSTANCE.buildAnimalList();

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

3.小结:

(1)饿汉式和懒汉式区别:

从名字上来说,饿汉和懒汉,

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

另外从以下两点再区分以下这两种方式:         

 ->1、线程安全:

                      饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

                 懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。

   ->2、资源加载和性能:

                          饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其                   资源已经初始化完成,而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能                   上会有些延迟,之后就和饿汉式一样了。

 

(2)什么是线程安全:

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

时间: 2024-10-09 04:32:48

Java常用的设计模式03:常用设计模式之单例模式的相关文章

Java开发中的23种设计模式介绍

设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

Java软件设计模式------单例设计模式

Java软件设计模式一共分为23种; 一般常用的有: 单例设计模式 装饰设计模式 工厂设计模式 单例设计模式(以后的用处:spring框架IOC,默认创建的对象都是单例的): 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案. 单例设计模式分为两种(饿汉式和懒汉式): ①饿汉式(常用): 特

Python/Java程序员面试必备常用问题解析与答案

转自AI算法联盟,理解python技术问题,以及一些常见的java面试中经常遇到的问题,这些面试问题分为四类: 是什么(what) 如何做(how) 说区别/谈优势(difference) 实践操作(practice) 1. 什么是Python? Python是一种编程语言,它有对象.模块.线程.异常处理和自动内存管理.可以加入与其他语言的对比.下面是回答这一问题的几个关键点: a. Python是一种解释型语言,python代码在运行之前不需要编译. b. Python是动态类型语言,在声明变

java开发中使用的适配器设计模式

package shipeiqidemo; /* * java中常见的一种设计模式:适配器 * 来源:一个子类要去实现接口,那么就必须要实现接口全部的抽象方法,有的时候抽象方法 * 很多,但是又用不着去全部实现,所以此时就定义一个抽象类去实现这个接口,然后通过子类去继承 * 这个抽象类,这个抽象类就简称适配器 */ public class shipeiqidemo { public static void main(String[] args) { //zhuozi zz = new zhuo

Java Web前端到后台常用框架介绍

转自: http://blog.csdn.net/u013142781/article/details/50922010 一.SpringMVC http://blog.csdn.net/evankaka/article/details/45501811 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spri

java设计模式系列之设计模式概要(1)

一.什么是设计模式 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是

JAVA:连接池技术说明以及MVC设计模式理解

JAVA:连接池技术说明以及MVC设计模式理解

Java设计模式—单例设计模式(Singleton Pattern)全然解析

转载请注明出处:http://blog.csdn.net/dmk877/article/details/50311791 相信大家都知道设计模式,听的最多的也应该是单例设计模式,这种模式也是在开发中用的最多的设计模式,可能有非常多人会写几种设计模式.那么你是否知道什么是设计模式?为什么会有单例设计模式即它的作用是什么?单例模式有哪些写法?对于这种问题.可能有部分童鞋并不能非常好的回答,没关系今天就和大家一起来具体的学习下单例设计模式,相信通过学习本篇你将对单例设计模式有个具体的理解. 如有谬误欢

Java IO流体系中常用的流分类

Java输入/输出流体系中常用的流分类 (表内容来自http://www.cnblogs.com/moonpool/p/5488463.html) 注:下表中带下划线的是抽象类,不能创建对象.粗体部分是节点流,其他就是常用的处理流. 流分类 使用分类 字节输入流 字节输出流 字符输入流 字符输出流   抽象基类 InputStream OutputStream Reader Writer 节点流 访问文件 FileInputStream FileOutStream FileReader File

Java 设计模式系列(五)单例模式

Java 设计模式系列(五)单例模式 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 一.懒汉式单例 /** * 懒汉式单例类.在第一次调用的时候实例化自己 * 1. 构造器私有化,避免外面直接创建对象 * 2. 声明一个私有的静态变量 * 3. 创建一个对外的公共静态方法访问该变量,如果没有变量就创建对象 */ public class Singleton { private Singleton() throws InterruptedException { Thre