单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。
单例模式一共有8种写法,各有自己的优缺点和适应场景。
package Singleton;
public class Singleton0 {
private final static Singleton0 INSTANCE=new Singleton0();
private Singleton0(){
}
public static Singleton0 getInstance(){
return INSTANCE;
}
/*
优点:这种写法比较简单,在类装载的时候就完成实例化,避免线程同步问题。
缺点:在类装载的时候完成实例化,没有达到Lazy Loading的效果,如果从未使用该实例会造成内存浪费
*/
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
package Singleton;
public class Singleton1 {
private static Singleton1 instance;
static {
instance=new Singleton1();
}
private Singleton1(){}
public static Singleton1 getInstance(){
return instance;
}
/*
将类实例化的过程放在了静态代码块中,也是在类装载的时候就执行静态代码块中的代码
*/
}
package Singleton;
public class Singleton2 {
private static Singleton2 singleton;
private Singleton2(){}
public static Singleton2 getInstance(){
if(singleton==null){
singleton=new Singleton2();
}
return singleton;
}
/*
改写法起到了Lazy Loading的效果,但是只能在单线程下使用,如果在多线程使用会产生多个实例
*/
}
package Singleton;
public class Singleton3 {
private static Singleton3 singleton;
private Singleton3(){}
public static synchronized Singleton3 getInstance(){
if(singleton==null){
singleton=new Singleton3();
}
return singleton;
}
/*
对getInstance()进行了线程同步可以解决线程不安全的问题
缺点:效率太低下,每个线程都想获得类的实例的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,
后面想要获得该类的实例,直接return就行,对整个方法进行同步效率低下
*/
}
在getInstance方法上加同步
package Singleton;
public class Singleton4 {
private static Singleton4 singleton;
private Singleton4(){}
public static Singleton4 getInstance(){
if(singleton==null){
synchronized (Singleton4.class){
singleton=new Singleton4();
}
}
return singleton;
}
/*
该方法改为同步产生实例化的代码块,但是这种方法起不到线程同步的作用。假如一个线程
进入了if内部还未来得及往下执行,另一个线程也通过了这个判断语句会产生多个实例。
*/
}
package Singleton;
public class Singleton6 {
private static volatile Singleton6 singleton;
private Singleton6(){
}
public static Singleton6 getInstance(){
if(singleton==null){
synchronized (Singleton6.class){
if(singleton==null){
singleton=new Singleton6();
}
}
}
return singleton;
}
/*
双重检查
优点:线程安全,延迟加载,效率较高
*/
}
package Singleton;
public class Singleton7 {
private Singleton7(){
}
private static class SingletonInstance{
private static final Singleton7 INSTANCE=new Singleton7();
}
public static Singleton7 getInstance(){
return SingletonInstance.INSTANCE;
}
/*
该方法与饿汉式机制类似。两者都采用类装载的机制来保证初始化实例时只有一个线程。
不同之处是饿汉式只要Singleton类被装载就会实例化,没有LazyLoading效果
静态内部类的方式在Singleton类被装载时并不会被实例化,而是在需要实例化时调用
getInstance()方法才会装载SingletonInstance类,从而完成Singleton类的实例化
类的静态属性只会在第一次加载类的时候就初始化,JVM帮助我们保证了线程的安全性,
在类的初始化时,别的线程是无法进入的
优点:避免了线程不安全,延迟加载,效率高
单例模式
优点:系统内存中只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,
使用单例模式可以提高系统性能。
缺点:当想要实例化一个单例类时,必须记住使用相应的获取对象的方法
适用场景:
需要频繁进行创建和销毁的对象
创建对象时耗时过多或者消耗资源过多,但又是经常使用的对象
工具类对象
频繁访问数据库或者文件的对象
*/
}
由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。
对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。