Java 实例解析单例模式

目录
  • 单例模式的介绍
    • 优点
    • 缺点
  • Synchronized
    • Synchronized示例
    • Synchronized与非Synchronized
  • Singleton
    • 第一个示例
    • 第二个示例
    • 第三个示例
    • 第四个示例
    • 第五个示例

单例模式的介绍

单例对象(Singleton)是一种常用的设计模式。在实际使用中,单例对象能保证在一个JVM中,该对象只存在一个实例存在。

优点

1、减少系统开销,提高系统性能
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力
3、避免对共享资源的多重占用

缺点

1、不适应用多变的对象
2、扩展困难
3、单例类的职责过重,在一定程度上违背了“单一职责原则”。

Synchronized

Synchronized示例

介绍单例模式前,我们现介绍一下Synchronized
示例如下:
建立一个内部类,并开启子线程,如果实例该类,则自动执行test1()方法

 class SynchronizedTest implements Runnable {
    private int count;

    public SynchronizedTest() {
        count = 0;
    }

    @Override
    public void run() {
        test1();
    }

    private void test1() {
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + count++);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

构造一个SynchronizedTest 对象,传入两个线程对象

 SynchronizedTest test = new SynchronizedTest();
        Thread thread1 = new Thread(test,"test1");
        Thread thread2 = new Thread(test,"test2");
        thread1.start();
        thread2.start();

由结果可知,当一个对象持有该代码块时,另一个线程访问不到被锁住的代码块,只要当前一线程执行完成,另一线程才能执行。

test1:0
test1:1
test1:2
test1:3
test1:4
test2:5
test2:6
test2:7
test2:8
test2:9

Synchronized与非Synchronized

建立一个内部类

 class SynchronizedTest implements Runnable {
    private int count;

    public SynchronizedTest() {
        count = 0;
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("S")) {
            test1();
        } else {
            test2();
        }
    }

    public void test1() {
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + (count++));
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void test2() {
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println(Thread.currentThread().getName() + ":" + count);
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 }
SynchronizedTest test = new SynchronizedTest();
        Thread thread1 = new Thread(test,"S");
        Thread thread2 = new Thread(test,"N");
        thread1.start();
        thread2.start();

由结果可知,一个线程访问Synchronized修饰的代码块,另一个线程访问非Synchronized代码块不受阻塞

S:0
N:1
N:2
S:1
N:2
S:2
S:3
N:4
S:4
N:5

Singleton

第一个示例

此示例实现了单例,但是如果放在多线程当中,将会漏洞百出
我们接着看下一个改良示例

public class Singleton {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static Singleton instance = null;
    /* 私有构造方法,防止被实例化 */
    private Singleton() {
    }
    /* 静态工程方法,创建实例 */
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

第二个示例

根据第一个示例,我们进行改良,加上Synchronized。
但是每次调用getInstance()方法时,都会对对象上锁,为了减少系统开销,我们一般在第一次创建对象的时候加锁,后面就不需要了
我们接着看下一个改良示例

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

第三个示例

我们对上一个示例进行改良,只有在instance == null的时候,也就是第一次创建对象的时候,执行加锁的区域。此种写法解决了上一个示例遗留的问题,但是在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。

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

第四个示例

此代码初看已经没有问题,如果在构造方法中出现异常,那么实例将得不到创建

public class Singleton {
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
  }
/* 获取实例 */
public static Singleton getInstance() {
return SingletonFactory.instance;
  }
}

第五个示例

   private static Singleton instance = null;
   private Singleton(){

    }
    public static Singleton getInstance(){
        if (instance == null){
            sync();
        }
        return instance;
    }
   private static synchronized void sync(){
        if (instance == null){
           instance = new Singleton();
            System.out.println("success");
       }
    }

到此这篇关于Java 实例解析单例模式的文章就介绍到这了,更多相关Java 单例模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅析Java单例设计模式(自写demo)

    目录 单例模式特点 单例模式优点 实现方式 饿汉式(线程安全) 懒汉式 单例模式特点 1.构造器私有 2.在一个Java应用程序中,可保证只有一个实例对象 3.只提供一个供外界调用的getInstance()方法 单例模式优点 1.减少某些对象的频繁创建,降低系统开销和内存占用 2.外部调用不使用new关键字,降低系统内存的使用频率 3.对于特殊的类,在系统中只能存在一个实例,否则系统无法正常运行,比如Controller 实现方式 这里简单介绍两种实现方式 饿汉式(线程安全) /** * @a

  • 深入理解Java设计模式之单例模式

    目录 一.什么是单例模式 二.单例模式的应用场景 三.单例模式的优缺点 四.单例模式的实现 1.饿汉式 2.懒汉式 3.双重加锁机制 4.静态初始化 五.总结 一.什么是单例模式 单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在. 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为.比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息.这种方式简

  • Java中的单例模式详解(完整篇)

    目录 前言 WHAT WHY 饿汉式 实现一:静态实例参数与静态代码块 实现二:静态内部类 懒汉式 错误一:单线程实现 错误二:同步方法 错误三:同步代码块之单次检查 错误四:同步代码块之双重检查 正确:双重检查+阻止重排序 枚举 场景 总结 前言 个人认为单例模式是设计模式中最简单也是最常用的一种,是对有限资源合理利用的一种方式.这个模式看似简单,但是其中蕴含了关于并发.类加载.序列化等一系列深层次的知识,如果理解不够深,就有可能在高并发时遇到难以预期的异常,或者会造成资源浪费. 所以本文会从

  • Java单例模式分析

    目录 单例模式 为什么要用单例 单例的关键点 几种写法 懒汉式 饿汉式 静态内部类写法 枚举单例 容器实现单例 参考 总结 单例模式 为什么要用单例 确保某个类只有一个对象,常用于访问数据库操作,服务的配置文件等. 单例的关键点 1.默认构造函数为private,复制构造函数和复制赋值函数也要private或=delete禁用.(做到无法被外部其他对象构造) 2.通过一个静态方法或枚举返回单例类对象. 3.确保多线程的环境下,单例类对象只有一个. 几种写法 本文主要介绍C++的懒汉式和饿汉式写法

  • Java 单例模式详细解释

    目录 饿汉式 懒汉式 懒汉式(加锁synchronized) 懒汉式(部分加锁synchronized) 懒汉式(DCL) 懒汉式(DCL)最终版 静态内部类 总结 饿汉式 /** * 饿汉式 * 类加载到内存后,就是实例化一个单例,JVM保证线程安全 * 简单使用:推荐使用 * 唯一缺点:不管用与不用,类加载时就会完成实例化 */ public class Demo01 { //开始先新建一个对象 private static final Demo01 INSTANCE = new Demo0

  • Java单例模式的6种实现方式详解

    目录 为什么使用单例模式 使用单例模式需要注意的关键点 单例模式的几种写法 1. 饿汉式 2. 懒汉式 3. DCL(Double CheckLock)实现单例 4. 静态内部类 5. 枚举单例 6. 容器实现单例 总结 为什么使用单例模式 需要确保某个类只要一个对象,或创建一个类需要消耗的资源过多,如访问IO和数据库操作等,这时就需要考虑使用单例模式了. 使用单例模式需要注意的关键点 将构造函数访问修饰符设置为private 通过一个静态方法或者枚举返回单例类对象 确保单例类的对象有且只有

  • Java 实例解析单例模式

    目录 单例模式的介绍 优点 缺点 Synchronized Synchronized示例 Synchronized与非Synchronized Singleton 第一个示例 第二个示例 第三个示例 第四个示例 第五个示例 单例模式的介绍 单例对象(Singleton)是一种常用的设计模式.在实际使用中,单例对象能保证在一个JVM中,该对象只存在一个实例存在. 优点 1.减少系统开销,提高系统性能 2.省去了new操作符,降低了系统内存的使用频率,减轻GC压力 3.避免对共享资源的多重占用 缺点

  • Java设计模式之单例模式实例分析

    本文实例讲述了Java设计模式之单例模式.分享给大家供大家参考,具体如下: 单例模式:(Singleton Pattern)是一个比较简单的模式,其定义如下: Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例) 单例模式,很简单的一个模式.其实在android开发中,很多地方都会用到单例模式,比如某些工具类.Json数

  • java编程多线程并发处理实例解析

    本文主要是通过一个银行用户取钱的实例,演示java编程多线程并发处理场景,具体如下. 从一个例子入手:实现一个银行账户取钱场景的实例代码. 第一个类:Account.java 账户类: package cn.edu.byr.test; public class Account { private String accountNo; private double balance; public Account(){ } public Account(String accountNo,double

  • Java多线程之volatile关键字及内存屏障实例解析

    前面一篇文章在介绍Java内存模型的三大特性(原子性.可见性.有序性)时,在可见性和有序性中都提到了volatile关键字,那这篇文章就来介绍volatile关键字的内存语义以及实现其特性的内存屏障. volatile是JVM提供的一种最轻量级的同步机制,因为Java内存模型为volatile定义特殊的访问规则,使其可以实现Java内存模型中的两大特性:可见性和有序性.正因为volatile关键字具有这两大特性,所以我们可以使用volatile关键字解决多线程中的某些同步问题. volatile

  • Java Web开发入门书籍实例解析(总结一)

    从事Java Web开发这一段时间来,对Java 面向对象的思想和MVC开发模式可以说已经熟悉了.我当前参与的项目使用的框架是Spring.SpringMVC.Hibernate.下面我们小编给大家整理一篇教程帮助大家学习javaweb相关知识,感兴趣的朋友可以参考下. 一.基本概念 1.1.WEB开发的相关知识 WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源. Internet上供外界访问的Web资源分为: 1.静态web资源(如html 页面):指w

  • Java编程探索之泛型擦除实例解析

    1.问题引出 源码: public static void main(String[] args) { List<Integer> a = new ArrayList<Integer>(); List<String> b = new ArrayList<String>(); System.out.println(a.getClass() == b.getClass());//结果true } 编译后L public static void main(Stri

  • Java对象的XML序列化与反序列化实例解析

    上一篇文章我们介绍了java实现的各种排序算法代码示例,本文我们看看Java对象的xml序列化与反序列化的相关内容,具体如下. XML是一种标准的数据交换规范,可以方便地用于在应用之间交换各类数据.如果能在Java对象和XML文档之间建立某种映射,例如Java对象的XML序列化和反序列化,那么就可以使Java的对象方便地与其他应用进行交换. java.beans包里面有两个类XMLEncoder和Decoder,分别用于将符合JabaBeans规范的Java对象以XML方式序列化和反序列化.以下

  • Java数组越界问题实例解析

    Java中数组初始化和OC其实是一样的,分为动态初始化和静态初始化, 动态初始化:指定长度,由系统给出初始化值 静态初始化:给出初始化值,由系统给出长度 在我们使用数组时最容易出现的就是数组越界问题,好了,这里有个简单的例子 int [][] array = {{1,2,3},{1,4}}; System.out.println(array[1][2]); 这是一个二维数组,很明显,数组越界了,控制台中会打印如下信息: Exception in thread "main" java.l

  • 实例解析Java中的构造器初始化

    1.初始化顺序 当Java创建一个对象时,系统先为该对象的所有实例属性分配内存(前提是该类已经被加载过了),接着程序开始对这些实例属性执行初始化,其初始化顺序是:先执行初始化块或声明属性时制定的初始值,再执行构造器里制定的初始值. 在类的内部,变量定义的先后顺序决定了初始化的顺序,即时变量散布于方法定义之间,它们仍就会在任何方法(包括构造器)被调用之前得到初始化. class Window { Window(int maker) { System.out.println("Window(&quo

  • Java设计模式之单例模式实例详解【懒汉式与饿汉式】

    本文实例讲述了Java设计模式之单例模式.分享给大家供大家参考,具体如下: 单例模式就是产生一个对象实例,供外外部访问. 它的应用场景就是在这个类在全局真资源需要统一访问,否则会造成混乱时,才有必要设计成单例. 懒汉式,就是在使用这个对象时,才去查看这个对象是否创建,如果没创建就马上创建,如果已经创建,就返回这个实例. 饿汉式,在加载这个类的时候就先创建好一个对象实例,等待调用. 两者的优缺点也能猜到,使用懒汉式,在反应速度上肯定要比饿汉式慢. 但是这个对象如果不被调用,那就节省了cpu和内存资

随机推荐