浅谈单例模式(Singleton)

简介: 单例模式的介绍优点缺点SynchronizedSynchronized示例Synchronized与非SynchronizedSingleton第一个示例第二个示例第三个示例第四个示例第五个示例


单例模式的介绍



单例对象(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");
       }
    }
相关文章
|
设计模式 存储
static应用之 单例设计模式(饿汉单例&懒汉单例)
本章我们来学习单例模式中的饿汉单例和懒汉单例,那么什么是单例模式呢?应用该模式的这个类永远只有一个实列,即一个类只能创建一个对象例如电脑上的任务管理器对象只需要一个就能解决问题,可以节省内存空间先定义一个类,把构造器私有如下图,先来看一下没有把构造器私有化的SingleInstance类,此时Test类中可以随意创建多个SingleInstance的实例化。 在SingleInstance类中用private修饰无参构造器,此时左边new方法报错了。我们在右边创建一个静态变量来存储对象,变量名为instan
53 0
|
5月前
|
安全 Java
线程安全的单例模式(Singleton)
线程安全的单例模式(Singleton)
|
6月前
|
SQL 安全 Java
懒汉式单例的3个坑
懒汉式单例的3个坑
|
3月前
|
设计模式 安全 Java
Singleton 的双重检查锁定
【8月更文挑战第23天】
42 2
|
安全 Java
懒汉式单例
懒汉式单例
|
安全 Java
饿汉式单例
饿汉式单例
|
安全 C#
单例模式(Singleton)
单例模式(Singleton)
68 0
单例模式(懒汉和饿汉)——独生子女挺好
单例模式(懒汉和饿汉)——独生子女挺好
|
设计模式 安全 Java
Java设计模式-单例模式(Singleton)
Java设计模式-单例模式(Singleton)
|
安全 Java
单例模式和多例模式(懒汉式和饿汉式)
单例模式和多例模式(懒汉式和饿汉式)
137 0