关键词:
转自
http://blog.csdn.net/cselmu9/article/details/51366946
在所有的设计模式中,单例模式是我们在项目开发中最为常见的设计模式之一,而单例模式有很多种实现方式,你是否都了解呢?高并发下如何保证单例模式的线程安全性呢?如何保证序列化后的单例对象在反序列化后任然是单例的呢?这些问题在看了本文之后都会一一的告诉你答案,赶快来阅读吧!
什么是单例模式?
在文章开始之前我们还是有必要介绍一下什么是单例模式。单例模式是为确保一个类只有一个实例,并为整个系统提供一个全局访问点的一种模式方法。
从概念中体现出了单例的一些特点:
(1)、在任何情况下,单例类永远只有一个实例存在
(2)、单例需要有能力为整个系统提供这一唯一实例
为了便于读者更好的理解这些概念,下面给出这么一段内容叙述:
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer
Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。
正是由于这个特点,单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境 下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。
各式各样的单例实现
温馨提示:本文叙述中涉及到的相关源码可以在这里进行下载源码,读者可免积分下载。
1、饿汉式单例
饿汉式单例是指在方法调用前,实例就已经创建好了。下面是实现代码:
- package org.mlinge.s01;
- public class MySingleton {
- private static MySingleton instance = new MySingleton();
- private MySingleton(){}
- public static MySingleton getInstance() {
- return instance;
- }
- }
- package org.mlinge.s01;
- public class MyThread extends Thread{
- @Override
- public void run() {
- System.out.println(MySingleton.getInstance().hashCode());
- }
- public static void main(String[] args) {
- MyThread[] mts = new MyThread[10];
- for(int i = 0 ; i < mts.length ; i++){
- mts[i] = new MyThread();
- }
- for (int j = 0; j < mts.length; j++) {
- mts[j].start();
- }
- }
- }
以上代码运行结果:
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
从运行结果可以看出实例变量额hashCode值一致,这说明对象是同一个,饿汉式单例实现了。
2、懒汉式单例
懒汉式单例是指在方法调用获取实例时才创建实例,因为相对饿汉式显得“不急迫”,所以被叫做“懒汉模式”。下面是实现代码:
- package org.mlinge.s02;
- public class MySingleton {
- private static MySingleton instance = null;
- private MySingleton(){}
- public static MySingleton getInstance() {
- if(instance == null){//懒汉式
- instance = new MySingleton();
- }
- return instance;
- }
- }
- package org.mlinge.s02;
- public class MySingleton {
- private static MySingleton instance = null;
- private MySingleton(){}
- public static MySingleton getInstance() {
- try {
- if(instance != null){//懒汉式
- }else{
- //创建实例之前可能会有一些准备性的耗时工作
- Thread.sleep(300);
- instance = new MySingleton();
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return instance;
- }
- }
- package org.mlinge.s02;
- public class MyThread extends Thread{
- @Override
- public void run() {
- System.out.println(MySingleton.getInstance().hashCode());
- }
- public static void main(String[] args) {
- MyThread[] mts = new MyThread[10];
- for(int i = 0 ; i < mts.length ; i++){
- mts[i] = new MyThread();
- }
- for (int j = 0; j < mts.length; j++) {
- mts[j].start();
- }
- }
- }
执行结果如下:
- 1210420568
- 1210420568
- 1935123450
- 1718900954
- 1481297610
- 1863264879
- 369539795
- 1210420568
- 1210420568
- 602269801
从这里执行结果可以看出,单例的线程安全性并没有得到保证,那要怎么解决呢?
3、线程安全的懒汉式单例
要保证线程安全,我们就得需要使用同步锁机制,下面就来看看我们如何一步步的解决 存在线程安全问题的懒汉式单例(错误的单例)。
(1)、 方法中声明synchronized关键字
出现非线程安全问题,是由于多个线程可以同时进入getInstance()方法,那么只需要对该方法进行synchronized的锁同步即可:
- package org.mlinge.s03;
- public class MySingleton {
- private static MySingleton instance = null;
- private MySingleton(){}
- public synchronized static MySingleton getInstance() {
- try {
- if(instance != null){//懒汉式
- }else{
- //创建实例之前可能会有一些准备性的耗时工作
- Thread.sleep(300);
- instance = new MySingleton();
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return instance;
- }
- }
此时任然使用前面验证多线程下执行情况的MyThread类来进行验证,将其放入到org.mlinge.s03包下运行,执行结果如下:
- 1689058373
- 1689058373
- 1689058373
- 1689058373
- 1689058373
- 1689058373
- 1689058373
- 1689058373
- 1689058373
- 1689058373
从执行结果上来看,问题已经解决了,但是这种实现方式的运行效率会很低。同步方法效率低,那我们考虑使用同步代码块来实现:
(2)、 同步代码块实现
- package org.mlinge.s03;
- public class MySingleton {
- private static MySingleton instance = null;
- private MySingleton(){}
- //public synchronized static MySingleton getInstance() {
- public static MySingleton getInstance() {
- try {
- synchronized (MySingleton.class) {
- if(instance != null){//懒汉式
- }else{
- //创建实例之前可能会有一些准备性的耗时工作
- Thread.sleep(300);
- instance = new MySingleton();
- }
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return instance;
- }
- }
(3)、 针对某些重要的代码来进行单独的同步(可能非线程安全)
针对某些重要的代码进行单独的同步,而不是全部进行同步,可以极大的提高执行效率,我们来看一下:
- package org.mlinge.s04;
- public class MySingleton {
- private static MySingleton instance = null;
- private MySingleton(){}
- public static MySingleton getInstance() {
- try {
- if(instance != null){//懒汉式
- }else{
- //创建实例之前可能会有一些准备性的耗时工作
- Thread.sleep(300);
- synchronized (MySingleton.class) {
- instance = new MySingleton();
- }
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return instance;
- }
- }
- 1481297610
- 397630378
- 1863264879
- 1210420568
- 1935123450
- 369539795
- 590202901
- 1718900954
- 1689058373
- 602269801
(4)、 Double Check Locking 双检查锁机制(推荐)
为了达到线程安全,又能提高代码执行效率,我们这里可以采用DCL的双检查锁机制来完成,代码实现如下:
- package org.mlinge.s05;
- public class MySingleton {
- //使用volatile关键字保其可见性
- volatile private static MySingleton instance = null;
- private MySingleton(){}
- public static MySingleton getInstance() {
- try {
- if(instance != null){//懒汉式
- }else{
- //创建实例之前可能会有一些准备性的耗时工作
- Thread.sleep(300);
- synchronized (MySingleton.class) {
- if(instance == null){//二次检查
- instance = new MySingleton();
- }
- }
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return instance;
- }
- }
- 369539795
- 369539795
- 369539795
- 369539795
- 369539795
- 369539795
- 369539795
- 369539795
- 369539795
- 369539795
这里在声明变量时使用了volatile关键字来保证其线程间的可见性;在同步代码块中使用二次检查,以保证其不被重复实例化。集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性。
4、使用静态内置类实现单例模式
DCL解决了多线程并发下的线程安全问题,其实使用其他方式也可以达到同样的效果,代码实现如下:
- package org.mlinge.s06;
- public class MySingleton {
- //内部类
- private static class MySingletonHandler{
- private static MySingleton instance = new MySingleton();
- }
- private MySingleton(){}
- public static MySingleton getInstance() {
- return MySingletonHandler.instance;
- }
- }
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
5、序列化与反序列化的单例模式实现
静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的。
代码实现如下:
- package org.mlinge.s07;
- import java.io.Serializable;
- public class MySingleton implements Serializable {
- private static final long serialVersionUID = 1L;
- //内部类
- private static class MySingletonHandler{
- private static MySingleton instance = new MySingleton();
- }
- private MySingleton(){}
- public static MySingleton getInstance() {
- return MySingletonHandler.instance;
- }
- }
- package org.mlinge.s07;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- public class SaveAndReadForSingleton {
- public static void main(String[] args) {
- MySingleton singleton = MySingleton.getInstance();
- File file = new File("MySingleton.txt");
- try {
- FileOutputStream fos = new FileOutputStream(file);
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- oos.writeObject(singleton);
- fos.close();
- oos.close();
- System.out.println(singleton.hashCode());
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- FileInputStream fis = new FileInputStream(file);
- ObjectInputStream ois = new ObjectInputStream(fis);
- MySingleton rSingleton = (MySingleton) ois.readObject();
- fis.close();
- ois.close();
- System.out.println(rSingleton.hashCode());
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
- 865113938
- 1442407170
解决办法就是在反序列化的过程中使用readResolve()方法,单例实现的代码如下:
- package org.mlinge.s07;
- import java.io.ObjectStreamException;
- import java.io.Serializable;
- public class MySingleton implements Serializable {
- private static final long serialVersionUID = 1L;
- //内部类
- private static class MySingletonHandler{
- private static MySingleton instance = new MySingleton();
- }
- private MySingleton(){}
- public static MySingleton getInstance() {
- return MySingletonHandler.instance;
- }
- //该方法在反序列化时会被调用,该方法不是接口定义的方法,有点儿约定俗成的感觉
- protected Object readResolve() throws ObjectStreamException {
- System.out.println("调用了readResolve方法!");
- return MySingletonHandler.instance;
- }
- }
- 865113938
- 调用了readResolve方法!
- 865113938
6、使用static代码块实现单例
静态代码块中的代码在使用类的时候就已经执行了,所以可以应用静态代码块的这个特性的实现单例设计模式。
- package org.mlinge.s08;
- public class MySingleton{
- private static MySingleton instance = null;
- private MySingleton(){}
- static{
- instance = new MySingleton();
- }
- public static MySingleton getInstance() {
- return instance;
- }
- }
- package org.mlinge.s08;
- public class MyThread extends Thread{
- @Override
- public void run() {
- for (int i = 0; i < 5; i++) {
- System.out.println(MySingleton.getInstance().hashCode());
- }
- }
- public static void main(String[] args) {
- MyThread[] mts = new MyThread[3];
- for(int i = 0 ; i < mts.length ; i++){
- mts[i] = new MyThread();
- }
- for (int j = 0; j < mts.length; j++) {
- mts[j].start();
- }
- }
- }
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
- 1718900954
7、使用枚举数据类型实现单例模式
枚举enum和静态代码块的特性相似,在使用枚举时,构造方法会被自动调用,利用这一特性也可以实现单例:
- package org.mlinge.s09;
- public enum EnumFactory{
- singletonFactory;
- private MySingleton instance;
- private EnumFactory(){//枚举类的构造方法在类加载是被实例化
- instance = new MySingleton();
- }
- public MySingleton getInstance(){
- return instance;
- }
- }
- class MySingleton{//需要获实现单例的类,比如数据库连接Connection
- public MySingleton(){}
- }
- package org.mlinge.s09;
- public class MyThread extends Thread{
- @Override
- public void run() {
- System.out.println(EnumFactory.singletonFactory.getInstance().hashCode());
- }
- public static void main(String[] args) {
- MyThread[] mts = new MyThread[10];
- for(int i = 0 ; i < mts.length ; i++){
- mts[i] = new MyThread();
- }
- for (int j = 0; j < mts.length; j++) {
- mts[j].start();
- }
- }
- }
- 1481297610
- 1481297610
- 1481297610
- 1481297610
- 1481297610
- 1481297610
- 1481297610
- 1481297610
- 1481297610
- 1481297610
8、完善使用enum枚举实现单例模式
不暴露枚举类实现细节的封装代码如下:
- package org.mlinge.s10;
- public class ClassFactory{
- private enum MyEnumSingleton{
- singletonFactory;
- private MySingleton instance;
- private MyEnumSingleton(){//枚举类的构造方法在类加载是被实例化
- instance = new MySingleton();
- }
- public MySingleton getInstance(){
- return instance;
- }
- }
- public static MySingleton getInstance(){
- return MyEnumSingleton.singletonFactory.getInstance();
- }
- }
- class MySingleton{//需要获实现单例的类,比如数据库连接Connection
- public MySingleton(){}
- }
- package org.mlinge.s10;
- public class MyThread extends Thread{
- @Override
- public void run() {
- System.out.println(ClassFactory.getInstance().hashCode());
- }
- public static void main(String[] args) {
- MyThread[] mts = new MyThread[10];
- for(int i = 0 ; i < mts.length ; i++){
- mts[i] = new MyThread();
- }
- for (int j = 0; j < mts.length; j++) {
- mts[j].start();
- }
- }
- }
- 1935123450
- 1935123450
- 1935123450
- 1935123450
- 1935123450
- 1935123450
- 1935123450
- 1935123450
- 1935123450
- 1935123450
以上就是本文要介绍的所有单例模式的实现,相信认真阅读的读者都已经明白文章开头所引入的那几个问题了,祝大家读得开心:-D!
备注:本文的编写思路和实例源码参照《Java多线程编程核心技术》-(高洪岩)一书中第六章的学习案例撰写。
---恢复内容结束---
单列模式(代码片段)
1.单列模式的定义保证一个类只有一个实例存在,同时提供能对该实例加以访问的静态方法。2.为什么要使用单列模式在整个程序空间使用全局变量,共享资源;大规模系统中,为了性能的考虑,需要节省对象的创建时间等。3.实... 查看详情
设计模式:单列设计模式模块方法设计模式
一、单列设计模式 1、设计模式:解决某一类问题的最行之有效的方法。 2、Java中有23中基本设计模式。 3、单列设计模式:一个类在内存中只有一个对象。 为了达到单列设计... 查看详情
单列模式
当所有实例中封装的数据相同时,只创建一个实例通过静态方法+静态字段当所有的实例中封装的内容相同时用单例模式 admin.py思路 students.py 查看详情
单列模式
单例模式单例模式可能是设计模式里面最简单的模式了,虽然简单,但在我们日常生活和编程中却经常接触到,本节我们一起来学习一下。单例模式(SingletonPattern)又称为单体模式,保证一个类只有一个实例,并提供一个访问... 查看详情
java单列模式
设计模式 单列模式的定义和作用目的:使得类的一个对象成为该类系统中的唯一实列; 定义:一个类有且仅有一个实例,并且自行实列化向整个系统提供?; 单列模式分为恶汉式 ... 查看详情
两种单列模式
publicclassSingleton{privatestaticSingletoninstance=newSingleton();privateSingleton(){}publicstaticSingletongetInstance(){returninstance;}}注解:初试化静态的instance创建一次。如果我们在Singleton类里面写一个静态的方法不需要创建实例,它仍然会早早 查看详情
单列模式
首页资讯精华论坛问答博客专栏群组更多▼知识库搜索您还未登录!登录注册cantellow 博客微博相册收藏留言关于我 cantellow单例模式的七种写法博客分类:设计模式多线程JVMServletIBMIDEA 转载请注明出处:http://cantellow.itey... 查看详情
七种写法的单列模式(代码片段)
目录七种写法的单列模式版本1:饿汉式版本2:多线程可能导致多个实例版本3:效率有待优化版本4:DoubleCheck可能出现空指针异常版本5:DoubleCheck版本6:通过静态内部类构造版本7:通过枚举构造七种写法的单列模式版本1:饿汉... 查看详情
单列模式
转自 http://blog.csdn.net/cselmu9/article/details/51366946 在所有的设计模式中,单例模式是我们在项目开发中最为常见的设计模式之一,而单例模式有很多种实现方式,你是否都了解呢?高并发下如何保证单例模式的线程安全... 查看详情
单列模式
Java单例模式单例模式的核心是保证一个类只有一个实例,并且提供一个访问实例的全局访问点。 单例模式优点由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产... 查看详情
单列模式
单例模式定义:定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。类型:类型:创建类模式类图:类图:图片1.1图片1.1singleton类图知识点:类图知识点:1.类图分为三部分,依次是类名、属性、方法2... 查看详情
类的单列模式
呵呵哒....classclss:#等于触发对象clss()__static=Nonedef__init__(self):pass@staticmethoddefstatic_def():ifclss.__static:returnclss.__staticelse:clss.__static=clss()returnclss.__staticdefyujia(self):print(‘11‘)d 查看详情
单列模式之线程安全实现(代码片段)
单例模式有五种写法:懒汉、饿汉、双重检验锁、静态内部类、枚举 懒汉式线程不安全publicclassSingletonprivatestaticSingletoninstance;privateSingleton()publicstaticSingletongetInstance()if(instance==null)instance=newSingleton();retur 查看详情
一些单列模式(代码片段)
因为最近的项目多次涉及到了单例模式所以特地来整理一下 包括项目使用的 还有网上其他的单例模式单例模式(SingletonPattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希... 查看详情
单列模式对比(代码片段)
单例模式。构造函数是私有的,通过一个共有的成员函数还调用这个构造函数,在多线程环境下,还需要对这个成员函数进行加锁。下面是4种单例的创建方式,最安全也最好的是第4种,使用内部类的方式。1、懒汉式单例,... 查看详情
单列模式的介绍
1. 单例模式的作用和应用场景(不全后面补充)?1.1保证里面Java应用程序中,只有一个实例的存在。比如:浏览网站显示当前在线人数,就是用一个单例的对象。2.单例模式的几种写法2.1注意事项:(1)下面的写法都是正确... 查看详情
设计模式之简单工厂和单列的区别
1.什么是设计模式 * 解析:针对特定问题而提出特定的解决方案 简单工厂不算23种设计模式中的一种,--只是一个抽象工厂   查看详情
java基础-单列设计模式
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。 单例模式有以下特点: 1、单例类只能有一个实例。 2、单例类必须自己创建... 查看详情