简介:单例模式就是采取一定的方法,保证在整个软件系统中,对某个类只存在一个实例对象,并且该类只提供一个静态的获取其对象实例的方法;
单例模式有8种方式:
- 饿汉式 (静态常量)
- 饿汉式 (静态代码块)
- 懒汉式 (线程不安全)
- 懒汉式 (线程安全,同步方法)
- 懒汉式 (线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
-----------------------------------------------------分 割 线--------------------------------------------------------
- 1. 饿汉式(静态常量)
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.本类内部创建实例
private final static Singleton singleton = new Singleton();
// 3.提供一个公开的静态的获取实例的方法
public static Singleton getSingleton() {
return singleton;
}
}
/**
* 单例模式-饿汉式(静态常量)
* 优缺点说明:
* 1.优点:写法比较简单,在类转载时实例化对象,避免了线程同步问题。
* 2.缺点:在类装载时完成实例化,没有Lazy Loading的效果,如果从始至终
* 没有实例化这个类,将会造成线程浪费;
* @param args
*/
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1==singleton2); //true
System.out.println(singleton1.hashCode()); //366712642
System.out.println(singleton2.hashCode()); //366712642
}
-----------------------------------------------------分 割 线--------------------------------------------------------
- 2.饿汉式(静态代码块)
class Singleton2 {
// 1.私有化构造器
private Singleton2() {
}
// 2.本类内部创建实例
private static Singleton2 singleton;
// 3.静态代码块中创建实例对象
static {
singleton = new Singleton2();
}
// 3.提供一个公开的静态的获取实例的方法
public static Singleton2 getSingleton() {
return singleton;
}
}
/**
* 饿汉式(静态代码块)
* 优缺点说明:
* 1.优点:写法比较简单,在静态代码块中实例化对象,避免了线程同步问题。
* 2.缺点:在类装载时完成实例化,没有Lazy Loading的效果,如果从始至终
* 没有实例化这个类,将会造成线程浪费;
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton2 singleton1 = Singleton2.getSingleton();
Singleton2 singleton2 = Singleton2.getSingleton();
System.out.println(singleton1 == singleton2); // true
System.out.println(singleton1.hashCode()); // 366712642
System.out.println(singleton2.hashCode()); // 366712642
}
-----------------------------------------------------分 割 线--------------------------------------------------------
- 3.懒汉式(线程不安全)
class Singleton3 {
// 1.私有化构造器
private Singleton3() {
}
// 2.本类内部创建实例
private static Singleton3 singleton;
// 3.提供一个公开的静态的获取实例的方法
public static Singleton3 getSingleton() {
if (singleton == null) {
singleton = new Singleton3();
}
return singleton;
}
}
/**
* 懒汉式(线程不安全) 优缺点说明:
* 1. 起到了Lazy Loading的效果,但是只能在单线程中使用
* 2. 如果在多线程下,if (singleton == null) 判断语句块,还未来的及往下执行,另一个线程也通过了这个判断
* 这时便会出现多个实例,所以在多线程的情况下,不推荐使用这种方式;
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton3 singleton1 = Singleton3.getSingleton();
Singleton3 singleton2 = Singleton3.getSingleton();
System.out.println(singleton1 == singleton2); // true
System.out.println(singleton1.hashCode()); // 366712642
System.out.println(singleton2.hashCode()); // 366712642
}
-----------------------------------------------------分 割 线--------------------------------------------------------
- 4.懒汉式(线程安全,同步方法)
class Singleton4 {
// 1.私有化构造器
private Singleton4() {
}
// 2.本类内部创建实例
private static Singleton4 singleton;
// 3.提供一个公开的静态的获取实例的方法
public static synchronized Singleton4 getSingleton() {
if (singleton == null) {
singleton = new Singleton4();
}
return singleton;
}
}
/**
* 懒汉式(线程安全,同步方法) 优缺点说明:
* 1. 解决了线程安全的问题
* 2. 同步代码块的效率太低了
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton4 singleton1 = Singleton4.getSingleton();
Singleton4 singleton2 = Singleton4.getSingleton();
System.out.println(singleton1 == singleton2); // true
System.out.println(singleton1.hashCode()); // 366712642
System.out.println(singleton2.hashCode()); // 366712642
}
-----------------------------------------------------分 割 线--------------------------------------------------------
- 5.懒汉式(线程安全,同步方法块)
class Singleton5 {
// 1.私有化构造器
private Singleton5() {
}
// 2.本类内部创建实例
private static Singleton5 singleton;
// 3.提供一个公开的静态的获取实例的方法
public static Singleton5 getSingleton() {
if (singleton == null) {
synchronized(Singleton5.class){
singleton = new Singleton5();
}
}
return singleton;
}
}
/**
* 懒汉式(线程安全,同步方法块)
* 1. 解决了线程安全的问题
* 2. 同步代码块的效率太低了
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton5 singleton1 = Singleton5.getSingleton();
Singleton5 singleton2 = Singleton5.getSingleton();
System.out.println(singleton1 == singleton2); // true
System.out.println(singleton1.hashCode()); // 366712642
System.out.println(singleton2.hashCode()); // 366712642
}
-----------------------------------------------------分 割 线--------------------------------------------------------
- 6.双重检查
class Singleton6 {
// 1.私有化构造器
private Singleton6() {
}
// 2.本类内部创建实例
private static volatile Singleton6 singleton;
// 3.提供一个公开的静态的获取实例的方法
public static Singleton6 getSingleton() {
if (singleton == null) {
synchronized(Singleton6.class){
if (singleton == null) {
singleton = new Singleton6();
}
}
}
return singleton;
}
}
/**
* 双重检查
* 1. 线程安全,延迟加载,效率高
* 2. 推荐使用这种单例模式
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton6 singleton1 = Singleton6.getSingleton();
Singleton6 singleton2 = Singleton6.getSingleton();
System.out.println(singleton1 == singleton2); // true
System.out.println(singleton1.hashCode()); // 366712642
System.out.println(singleton2.hashCode()); // 366712642
}
-----------------------------------------------------分 割 线--------------------------------------------------------
- 7.静态内部类
class Singleton7 {
// 1.私有化构造器
private Singleton7() {
}
// 2.静态内部类创建对象
private static class SingletonInstance {
private static final Singleton7 INSTANCE = new Singleton7();
}
// 3.提供一个公开的静态的获取实例的方法
public static Singleton7 getSingleton() {
return SingletonInstance.INSTANCE;
}
}
/**
* 静态内部类
* 优缺点: 1.这种方式采用类加载的机制来保证初始化实例时只有一个线程 2.静态内部类方式在 Singleton7
* 类被装载时,不会立即初始化,而是需要在实例化时,调用 getSingleton 才会调用 SingletonInstance
* 方法,从而完成初始化;
* 3.类的静态方法只会在类的第一次加载时初始化,所以在这里,JVM机制帮我们保证了线程的安全性,在类进行初始化时,别的线程无法进入;
* 4.优点:为了避免线程不安全,利用静态内部类特点实现延迟加载,效率高;
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton7 singleton1 = Singleton7.getSingleton();
Singleton7 singleton2 = Singleton7.getSingleton();
System.out.println(singleton1 == singleton2); // true
System.out.println(singleton1.hashCode()); // 366712642
System.out.println(singleton2.hashCode()); // 366712642
}
-----------------------------------------------------分 割 线--------------------------------------------------------
- 8.枚举
enum Singleton8{
INSTANCE;
public void sayOK(){
System.out.println(" OK! ");
}
}
/**
* 枚举
* 优缺点:借助JDK 1.5中添加枚举来实现单例模式,不仅能避免多线程同步问题,还能防止反序列化重新创建对象
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton8 singleton1 = Singleton8.INSTANCE;
Singleton8 singleton2 = Singleton8.INSTANCE;
System.out.println(singleton1 == singleton2); // true
System.out.println(singleton1.hashCode()); // 366712642
System.out.println(singleton2.hashCode()); // 366712642
singleton1.sayOK();
}
-----------------------------------------------------分 割 线--------------------------------------------------------
总结:
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要反复频繁创建销毁的对象,使用单例模式可以提升系统的性能;
- 当想实例化一个单例类的时候,记住必须使用相应的获取对象的方法,不能使用new;
- 单例模式的使用场景:
- 需要频繁创建和销毁的对象;
- 创建对象耗时较长或者资源较多,但又经常使用的对象,工具类对象,频繁访问数据库或文件的对象;