创建单例对象
Singleton类是单例的,每次调用该类的getInstance()方法都将得到相同的实例,目前该类中这个方法尚未完成,请将其补充完整,使得main()函数中的判断返回真(不考虑线程安全)。
什么是单例模式?
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
三种模式实现单例对象
1.饿汉模式–线程不安全
在类加载时已经创建好该单例对象,等待被程序使用
import java.util.Scanner;
//饿汉模式
public class Main {
public static void main(String[] args) {
Singleton s1= Singleton.getInstance();
Singleton s2= Singleton.getInstance();
System.out.println(s1==s2);
}
}
class Singleton {
private static Singleton instance = new Singleton(); //用private static字段创建唯一指定的实例
private Singleton(){
//用private使得构造方法无法访问
}
public static Singleton getInstance(){
return instance;
}
}
饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
2.懒汉模式–线程不安全
在真正需要使用对象时才去创建该单例类对象
import java.util.Scanner;
//懒汉模式
public class Main {
public static void main(String[] args) {
Singleton s1= Singleton.getInstance();
Singleton s2= Singleton.getInstance();
System.out.println(s1==s2);
}
}
class Singleton {
private static Singleton instance = new Singleton(); //用private static字段创建唯一指定的实例
private Singleton(){
//用private使得构造方法无法访问
}
public static Singleton getInstance(){
if(instance==null){
如果为空,才进行实例化,保证一个类只有一个实例,存在线程安全的问题
instance =new Singleton();
}
return instance;
}
}
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。
3.双锁模式–线程安全
如果使用懒汉模式,两个线程同时判断singleton为空,那么它们都会去实例化一个Singleton对象,这就变成双例了。所以,我们要解决的是线程安全问题,最好的解决方法就是在方法上加锁,或者是对类对象加锁
public class Main {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
class Singleton {
private volatile static Singleton instance;//使用volatile防止指令重排
//指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能
private Singleton() {
}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
饿汉式和 懒汉式加上synchronized也是线程安全的。
总结
01 单例模式常见的写法有两种:懒汉式、饿汉式
02 懒汉式:在需要用到对象时才实例化对象,正确的实现方式是:Double Check + Lock,解决了并发安全和性能低下问题
03 饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。
04 在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;
05 如果对内存要求不高使用饿汉式写法,因为简单不易出错,且没有任何并发安全和性能问题
06 为了防止多线程环境下,因为指令重排序导致变量报NPE,需要在单例对象上添加volatile关键字防止指令重排序