浅谈单例模式
最近在学设计模式,感受颇深,闲来无聊,记录一下单例模式的学习。
单例模式:采取一定的方法保证在整个软件系统中,对一个类只能存在一个对象实例,且只提供一个能取得对象实例的静态方法
。
单例模式的八种方式:
1)饿汉式(静态常量)
2)饿汉式(静态代码块)
3)懒汉式(线程不安全)
4)懒汉式(线程安全,同步代码块)
5)懒汉式(线程线程安全,同步方法)
6)双重检查
7)静态内部类
8)枚举
1.饿汉式,静态常量(可以使用)
优点:在类加载的时候就实例化对象,避免了多线程的同步问题
缺点:如果软件系统从始至终未使用过该实例,则造成内存浪费
public class Singleton {
//1.提供一个私有的构造器防止new对象的实例
private Singleton() {}
//2.提供一个静态变量
private static final Singleton instance = new Singleton();
//3.提供一个静态方法获取实例
public static Singleton getInstance(){
return instance;
}
}
2.饿汉式,静态代码块(可以使用)
优点和缺点跟静态常量方式一致
public class Singleton {
private Singleton(){}
private static Singleton instance;
// 静态代码块,在类被加载时进行实例化
static {
instance = new Singleton();
}
// 对外提供静态方法获取实例
public static Singleton getInstance(){
return instance;
}
}
3.懒汉式,线程不安全的写法(实际开发中不能使用)
public class Singleton {
private Singleton() {
}
private static Singleton instance;
// 在程序第一次需要使用时才创建实例,达到懒汉式的效果(即懒加载)
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:在运行是第一次使用到该实例时才进行实例化,达到了懒加载的效果
缺点:线程不安全,在多线程情况下,如果第一个线程进入 if 后还没来得及
及往下执行,第二个线程也进入了 if 则两个线程都创建了实例。造成线程不安全
4.懒汉式,线程安全的写法(可以使用)
public class Singleton{
private Singleton(){}
private static Singleton instance;
/**
* 在程序第一次使用时进行实例化,达到懒加载效果,且进行同步,线程安全
* 但是效率太低,每次调用时都进行加锁,实际上只需在第一次时进行加锁
*/
public static synchronized Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
优点:在程序第一次使用该实例时才进行实例化,达到懒加载的效果,
缺点:效率太低,多线程情况下,每个线程都对获取实例的方法进行加锁
操纵,可能会造成阻塞。
5.懒汉式,线程不安全的写法(实际开发中不能使用)
public class Singleton {
private Singleton() {}
private static Singleton instance;
/**
* 在多线程模式下,当第一个线程进入到if里面还没来得及往下执行时,第二个线程也进入到if,会创建两个实例
* 在实际开发中,不能使用
* @return
*/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
此写法与懒加载同步方法的写法差不多,但存在线程不安全的情况,当多
线程情况下,第一个线程进入 if 后还没来得及往下执行,第二个线程也进入了
if 只不过在同步代码块外等待,当第一个线程释放锁后,第二个线程也进入
同步代码块,也进行了实例化,系统中该类就存在多个实例。
6.双重检查(在实际开发中推荐使用)
public class Singleton{
private Singleton(){}
private static volatile Singleton instance;
public static Singleton getInstance(){
// 进行两次判断
if (instance == null){
synchronized (Singleton.class){
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查,实例化代码只需执行一次避免了反复进行同步
解决了懒加载的同时,避免了多线程问题的发生
7.静态内部类(推荐使用)
public class Singleton{
// 提供私有构造器,防止被new产生新对象
private Singleton(){}
private static class InnerSingleton{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return InnerSingleton.INSTANCE;
}
}
优点:在Sinleton类进行加载的时候不会导致静态内部类进行加载 ,
从而不会使Sinleton实例化(利用了类加载机制,加载时线程时安全的
不会出现线程安全性问题。)
在调用Singleton的静态方法时导致内部类被加载才对Singleton进行实例化
达到了懒加载的效果,且在内部类进行
8.枚举(实际开发中推荐使用)
public class SingletonTest {
public static void main(String[] args) {
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println(singleton1 ==singleton2);
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
}
}
enum Singleton{
INSTANCE;
}
借助枚举来创建单例,不仅能解决多线程的问题,还能防止反序列化重新创建新的对象
单例模式记录完毕,如发现有误,望指正,谢谢。