Java单例模式学习笔记

开发 后端
Singleton 模式的宗旨在于确保某个类只有一个实例,别且为之提供一个全局访问点。为了防止其他工作人员实例化我们的类。

Singleton 模式的宗旨在于确保某个类只有一个实例,别且为之提供一个全局访问点。为了防止其他工作人员实例化我们的类,

可以为该类创建唯一一个构造器,并将构造器的可见设置为私有。值得注意的是,如果我们创建了其他的非私有的构造器,或者根本没有为该类提

供构造器,那么其他人员还是能实例化我们的类。 如果不希望提前创建单例对象,我们可以等到第一次使用该单例对象的时候在创建它,即

滞后初始化。滞后初始化单例对象有两个理由:

1.也许在静态初始化时间,你没有关于如何初始化单例对象的足够信息。

2.选择滞后初始化单例的目的也许为了等待资源,诸如数据库连接,尤其是在某些特定会话中不需要这个单例的应用程序中。

如果在多线程环境中对单例采用滞后初始化,那么我们必须小心防止多个线程同时初始化该

通常单例模式在Java语言中,有两种构建方式:

懒汉方式:指全局的单例实例在第一次被使用时构建。延迟初始化。

饿汉方式:指全局的单例实例在类装载时构建。 急切初始化。

1,饿汉式单例类

 

  1. public class Singleton1 {  
  2.       
  3.     private Singleton1() {  
  4.     }  
  5.     // 在自己内部定义自己一个实例.  
  6.     // 注意这是private 只供内部调用  
  7.  
  8.     private static Singleton1 instance = new Singleton1();  
  9.  
  10.     /** *//**  
  11.      *  这里提供了一个供外部访问本class的静态方法,可以直接访问    
  12.      * @return  
  13.      */ 
  14.     public static Singleton1 getInstance() {  
  15.         return instance;  
  16.     }  

 

 

2,懒汉式单例类

 

  1. public class Singleton2 {  
  2.  
  3.     private static Singleton2 instance = null;  
  4.     /** *//**  
  5.      * 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
  6.      * 使用时生成实例,提高了效率!  
  7.      * @return  
  8.      */ 
  9.     public static  Singleton2 getInstance() {      
  10.         if (instance == null)  
  11.             instance = new Singleton2();  
  12.         return instance;  
  13.     }  

 

 

下面主要多线程问题,在懒汉单例中,单线程是没有问题的,但多线程时就会有可能出现两个或者以上的Singletion2实例的情况。

例如:线程1在判断instance==null为真,扫行new操作时,在执行new操作之前,判断为真之后,线程2正好执行判断操作,这时instance还为null.因此,线程2也会执行new操作。以此类推,在高并发下面,就可能存在两个或者以上的Singletion2的实例。显然,这是不正确的。

因此改变代码如下:

 

  1. public class Singleton3 {  
  2.  
  3.     private static Singleton3 instance = null;  
  4.     /** *//**  
  5.      * 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
  6.      * 使用时生成实例,提高了效率!  
  7.      * 为了多线程不出错,加入了同步标志  
  8.      * @return  
  9.      */ 
  10.     public static synchronized  Singleton3 getInstance() {      
  11.         if (instance == null)  
  12.             instance = new Singleton3();  
  13.         return instance;  
  14.     }  
  15.  

 

 

但这样又产生了一个问题,每次获取实例时方法都是同步的,显然性能很受影响的,所以继续更改代码如下:

先记一下:volatile(网上抄的)

volatile, 用更低的代价替代同步

为什么使用volatile比同步代价更低?

同步的代价, 主要由其覆盖范围决定, 如果可以降低同步的覆盖范围, 则可以大幅提升程序性能.

而volatile的覆盖范围仅仅变量级别的. 因此它的同步代价很低.

volatile原理是什么?

volatile的语义, 其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我.(工作内存详见java内存模型)

因此, 当多核或多线程在访问该变量时, 都将直接操作主存, 这从本质上, 做到了变量共享.

volatile的有什么优势?

1, 更大的程序吞吐量

2, 更少的代码实现多线程

3, 程序的伸缩性较好

4, 比较好理解, 无需太高的学习成本

volatile有什么劣势?

1, 容易出问题

2, 比较难设计

volatile使用jdk要求1.5版本及1.5以上。

改进后的代码如下(又叫双重加锁):

 

  1. public class Singleton4 {  
  2.    private static volatile Singleton4 instance;  
  3.     /** *//**  
  4.      * 双重加锁实现多线程运用和性能优化  
  5.      * @return  
  6.      */ 
  7.     public static Singleton4 getInstance()  
  8.     {  
  9.       if (instance == null)  
  10.       {  
  11.         synchronized(Singleton4.class) {  //1  
  12.           if (instance == null)          //2  
  13.             instance = new Singleton4();  //3  
  14.         }  
  15.       }  
  16.       return instance;  
  17.     }  
  18. }  
[[20017]]

 

参考自第联网和head first设计模式。

【编辑推荐】

  1. Google首席架构师谈Java的命运
  2. JVM安装与Tomcat配置具体步骤
  3. 巧解Tomcat中JVM内存溢出问题
  4. Oracle发布Java移动开发框架ADF
  5. 揭露JDK,JRE,JVM三者不为人知的隐情
责任编辑:金贺 来源: blogjava
相关推荐

2020-09-16 12:18:28

GoJava模式

2021-03-02 08:50:31

设计单例模式

2021-02-01 10:01:58

设计模式 Java单例模式

2022-09-29 08:39:37

架构

2013-11-26 16:20:26

Android设计模式

2016-03-28 10:23:11

Android设计单例

2021-09-07 10:44:35

异步单例模式

2022-06-07 08:55:04

Golang单例模式语言

2021-02-07 23:58:10

单例模式对象

2022-02-06 22:30:36

前端设计模式

2024-03-06 13:19:19

工厂模式Python函数

2024-02-04 12:04:17

2016-10-09 09:37:49

javascript单例模式

2015-09-06 11:07:52

C++设计模式单例模式

2011-06-28 15:18:45

Qt 单例模式

2023-11-21 21:39:38

单例模式音频管理器

2015-01-14 13:26:58

AndroidJava单例

2022-05-23 07:35:15

单例模式懒汉模式静态内部类

2010-02-05 17:00:06

C++单例模式

2021-07-27 07:31:16

单例模式关键字
点赞
收藏

51CTO技术栈公众号