设计模式(一)单例模式

单例模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。



优点:1. 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时产生一个单例对象,然后永久驻留内存的方式来解决。

2. 单例模式可以在系统设置全局的访问点,例如可以设计一个单例类,负责所有数据表的映射处理。

常见的五种单例模式实现方式:

饿汉式:线程安全,调用效率高。但是,不能延时加载。

懒汉式:线程安全,调用效率不高。但是,可以延时加载。

双重检测锁式:由于JVM底层内部模型原因,偶尔会出现问题,不建议使用。

静态内部类式:线程安全,调用效率高。但是,可以延时加载。

枚举单例:线程安全,调用效率高,不能延时加载。

饿汉式:

1 public class Singleton{
2     private static Singleton singleton = new Singleton();
3     private Singleton(){}
4     public static Singleton getInstance(){
5         return singleton;
6     }
7 }

懒汉式:

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

双重检测锁式:

 1 public class Singleton{
 2     private static Singleton singleton;
 3     private Singleton(){}
 4     public static Singleton getInstance(){
 5         if(singleton == null){
 6             synchronized(Singleton.class){
 7                 if(singleton == null){
 8                     singleton = new Singleton();
 9                 }
10             }
11         }
12         return singleton;
13     }
14 }

静态内部类式:

1 public class Singleton{
2     private static class SingletonClassInstance{
3         private static final Singleton singleton = new Singleton();
4     }
5     public static Singleton getInstance(){
6         return SingletonClassInstance.singleton;
7     }
8     private Singleton(){}
9 }

注:外部类没有static属性,则不会像饿汉式那样立即加载对象。只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。singleton是static final(final可省略)类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性。兼备了并发高效调用和延迟加载的优势。

枚举单例:

 1 public enum Singleton{
 2     //定义一个枚举的元素,它就代表了Singleton的一个实例
 3     INSTANCE;
 4     public void singletonOperation(){
 5         //功能处理
 6     }
 7 }
 8
 9 public class GOF {
10     public static void main(String[] args){
11         Singleton s1 = Singleton.INSTANCE;
12         Singleton s2 = Singleton.INSTANCE;
13         System.out.println(s1==s2);
14     }
15 }
16
17 打印结果:true

注:实现简单。枚举本身就是单例模式。由JVM从根本上提供保障,避免通过反射和反序列化的漏洞。但无延迟加载。

五种如何选用:

单例对象 占用资源少,不需要延时加载:枚举式好于饿汉式

单例对象 占用资源大,需要延时加载:静态内部类好于懒汉式

问题

反射和反序列化可以破解上面几种(不包含枚举式)实现方式。

1. 例:通过反射的方式直接调用私有构造器。

代码中的SingletonDemo1为饿汉式

 1 import java.lang.reflect.Constructor;
 2
 3 public class SingletonDemo1_1 {
 4     public static void main(String[] args)throws Exception{
 5         SingletonDemo1 s1 = SingletonDemo1.getInstance();
 6         SingletonDemo1 s2 = SingletonDemo1.getInstance();
 7         System.out.println(s1 == s2);
 8
 9         Class<SingletonDemo1> clazz = (Class<SingletonDemo1>)SingletonDemo1.class;
10         Constructor<SingletonDemo1> c = clazz.getDeclaredConstructor(null);
11         c.setAccessible(true);
12         SingletonDemo1 s3 = c.newInstance();
13         SingletonDemo1 s4 = c.newInstance();
14         System.out.println(s3 == s4);
15     }
16 }
17
18 打印结果:
19 true
20 false

解决方法:可以在构造方法中抛出异常控制。

修改后的饿汉式为:

 1 public class SingletonDemo1{
 2     private static SingletonDemo1 single = new SingletonDemo1();
 3     private SingletonDemo1(){
 4         if(single != null){
 5             throw new RuntimeException();
 6         }
 7     }
 8     public static SingletonDemo1 getInstance(){
 9         return single;
10     }
11 }

2. 例:通过反序列化的方式构造多个对象。

 1 import java.io.FileInputStream;
 2 import java.io.FileOutputStream;
 3 import java.io.ObjectInputStream;
 4 import java.io.ObjectOutputStream;
 5
 6 public class SingletonDemo1_1 {
 7     public static void main(String[] args)throws Exception{
 8         SingletonDemo1 s1 = SingletonDemo1.getInstance();
 9         SingletonDemo1 s2 = SingletonDemo1.getInstance();
10         System.out.println(s1 == s2);
11
12         FileOutputStream fos = new FileOutputStream("G:/a.txt");
13         ObjectOutputStream oos = new ObjectOutputStream(fos);
14         oos.writeObject(s1);
15         oos.close();
16         fos.close();
17
18         ObjectInputStream ois = new ObjectInputStream(new FileInputStream("G:/a.txt"));
19         SingletonDemo1 s3 = (SingletonDemo1)ois.readObject();
20         System.out.println(s2 == s3);
21     }
22 }
23
24 打印结果:
25 true
26 false

解决方法:定义readResolve()方法,则直接返回此方法指定的对象,而不需要单独再创建新对象。

如:

 1 import java.io.ObjectStreamException;
 2 import java.io.Serializable;
 3
 4 public class SingletonDemo1 implements Serializable {
 5     private static SingletonDemo1 single = new SingletonDemo1();
 6     private SingletonDemo1(){
 7         if(single != null){
 8             throw new RuntimeException();
 9         }
10     }
11     public static SingletonDemo1 getInstance(){
12         return single;
13     }
14     private Object readResolve() throws ObjectStreamException{
15         return single;
16     }
17 }

五种单例模式在多线程环境下的效率测试:

CountDownLatch同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

countDown() 当前线程调此方法,则计数减一(建议放在finally里执行)

await() 调用此方法会一直阻塞当前线程,直到计时器的值为0

 1 import java.util.concurrent.CountDownLatch;
 2
 3
 4 public class Cilent {
 5     public static void main(String[] args)throws Exception{
 6         long start = System.currentTimeMillis();
 7         int threadNum = 10;
 8         final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
 9         for(int i = 0; i < 10; i++){
10             new Thread(new Runnable(){
11                 @Override
12                 public void run() {
13                     for(int i = 0; i < 100000; i++){
14                         Object o = SingletonDemo1.getInstance();
15                     }
16                     countDownLatch.countDown();
17                 }
18             }).start();
19         }
20         countDownLatch.await();
21         long end = System.currentTimeMillis();
22         System.out.println("总耗时 = " + (end - start));
23     }
24 }

经过测试 懒汉式耗时最多,其次是双重检查锁式,其他相差不大。

时间: 2024-10-12 09:32:45

设计模式(一)单例模式的相关文章

Java设计模式:单例模式

概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,但只能

[转]JAVA设计模式之单例模式

原文地址:http://blog.csdn.net/jason0539/article/details/23297037 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话

设计模式 之 单例模式

单例模式思路: 私有化构造方法: 防止实例化 私有化克隆方法: 防止克隆 私有化静态属性: 保存对象 公有化静态方法: 获取对象 代码: <?php //设计模式:单例模式 class Singleton { //私有化静态属性:用于保存对象 private static $obj; //私有化构造方法 private function __construct(){} //公有化静态方法:用于实例化对象 public static function getObj() { //判断对象是否存在 i

设计模式实例学习-单例模式(Android中的使用场景)

1.设计模式实例-单例模式 单例模式,故名思议,是指在一个类中通过设置静态使得其仅创造一个唯一的实例.这样设置的目的是满足开发者的希望--这个类只需要被实例化创建一次,同时因为其为静态的缘故,加载的速度也应该快于正常实例化一个类的速度(理论上). 在Android开发中,当我们需要创建一个Fragment的时候常常会用到这样的模式,没有代码的学习是虚无的,接下来亮代码学习: public class SelectFrame extends Fragment { private final sta

(九)JAVA设计模式之单例模式

JAVA设计模式之单例模式 一.单例模式的介绍 Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点.     全局对象和Singleton模式有本质的区别,因为大量使用全局对象会使得程序质量降低,而且有些编程语言根本不支持全局变量.最重要的是传统的全局对象并不能阻止一个类被实例化多次. 二.单例模式的特点 单例类只能有一个实例 单例类必须自己创建自己的唯一实例. 单例类必须给所有其他对象提供这一实例.

C#设计模式(1)——单例模式

一.引言 最近在设计模式的一些内容,主要的参考书籍是<Head First 设计模式>,同时在学习过程中也查看了很多博客园中关于设计模式的一些文章的,在这里记录下我的一些学习笔记,一是为了帮助我更深入地理解设计模式,二同时可以给一些初学设计模式的朋友一些参考.首先我介绍的是设计模式中比较简单的一个模式——单例模式(因为这里只牵涉到一个类) 二.单例模式的介绍 说到单例模式,大家第一反应应该就是——什么是单例模式?,从“单例”字面意思上理解为——一个类只有一个实例,所以单例模式也就是保证一个类只

.NET设计模式之(单例模式)

1.单例模式,一个类只能new一个对象 2.举例,资源管理器,文件管理器,地球等: 3.创建单例: (1)创建一个Earth类 class Earth { public Earth() { } } (2)将构造函数 私有化 class Earth { private Earth() { } } (3)声明一个静态私有的字段,初始化一个实例 class Earth { private static Earth instance=new Earth(); private Earth() { } }

Java 设计模式(3)单例模式

前言 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,

设计模式【单例模式】

Java中单例模式是一种常见的设计模式,单例模式分为:饿汉式单例模式.懒汉式单例模式.登记式单例模式.枚举式单例模式.作为对象的常见模式的单例模式,确保某一类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类必须给所有其他对象提供这一实例. 举例说明:在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实

重头开始学23种设计模式:单例模式

最近感觉做程序又开始浑浑噩噩,对设计模式和算法基本了解,但基本不会用.所以打算最近1个月把设计模式和算法重新,温故而知新下. 首先从程序开发经常涉及到的23种设计模式开始,希望这次能更加熟练的运用设计模式来加强自己的开发能力. 首先从单例模式开始: 单例模式在我的理解是对程序对象的缓存,防止不断new,保持对象唯一性,提高程序性能. namespace SinglePattern { class Program { static void Main(string[] args) { for (i