单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
单例模式分为:饿汉模式和懒汉模式
一 饿汉模式
1 饿汉模式 ---静态常量(可用)
/**
* 单例模式 --饿汉模式--静态常量(可以用)
* y:在类加载的时候就进行了类的实例化,避免了线程的同步问题
* n:同时,在类加载的时候就进行了实例化,没有达到懒加载的效果,万一之后不用,就会浪费内存
*
*/
public class SingletonTest1 {
private static final SingletonTest1 instance=new SingletonTest1();
private SingletonTest1() {}
public static SingletonTest1 getInstance() {
return instance;
}
}
y:在类加载的时候就进行了类的实例化,避免了线程的同步问题
n:同时,在类加载的时候就进行了实例化,没有达到懒加载的效果,万一之后不用,就会浪费内存
2 饿汉模式 ---静态代码块(可用)
/**
* 单例模式 --饿汉模式--静态代码块的使用(可以用)
* 和静态常量差不多,在类加载的时候加载静态代码块进行实例化
*
*/
public class SingletonTest2 {
private static SingletonTest2 instance=null;
static {
instance=new SingletonTest2();
}
private SingletonTest2() {}
public static SingletonTest2 getInstance() {
return instance;
}
}
和静态常量差不多,在类加载的时候加载静态代码块进行实例化
二 懒汉模式
1 懒汉模式:普通模式(不用,线程不安全)
/**
* 懒汉模式 --普通模式(不用,线程不安全)
* 刚开始不进行实例化,等需要的时候--》有就调用,没有就进行实例化。
* y:这个模式实现了懒加载.单线程下是有效的
* n:但是在多线程下不安全----当一个线程在if(instance == null) 这个判断还没有往下进行实例化,又有一个线程进行判断了,这个时候就会进行两次实例化,不再单例。
*/
public class SingletonTest3 {
private static SingletonTest3 instance = null;
private SingletonTest3() {}
public static SingletonTest3 getInstance() {
if(instance == null) {
instance = new SingletonTest3();
}
return instance;
}
}
刚开始不进行实例化,等需要的时候--》有就调用,没有就进行实例化。
y:这个模式实现了懒加载.单线程下是有效的
n:但是在多线程下不安全----当一个线程在if(instance == null) 这个判断还没有往下进行实例化,又有一个线程进行判断了,这个时候就会进行两次实例化,不再单例。
2 懒汉模式:--使用锁(同步方法-锁加在方法上) (可用,不推荐)
public class SingletonTest4 {
private static SingletonTest4 instance = null;
private SingletonTest4() {}
public static synchronized SingletonTest4 getInstance() {
if(instance == null) {
instance = new SingletonTest4();
}
return instance;
}
}
y:这种懒汉模式实现了线程同步,不会发生因为多线程的问题出现多个实例的问题
n: 但是这种方式的效率太低了,因为主要是对实例化进行线程同步的,在instance不是null之后,直接return instance不需要进行同步加锁。
3 懒汉模式 :--同步代码块(在代码块上加锁)(不可用)
public class SingletonTest5 {
private static SingletonTest5 instance=null;
private SingletonTest5() {}
public static SingletonTest5 getInstance() {
if(instance == null) {
synchronized (SingletonTest5.class) {
instance=new SingletonTest5();
}
}
return instance;
}
}
由于同步方法的效率太低,所以使用同步代码块,但是使用这种和SingletonTest3出现相同的问题,在if(instance == null)那块线程不同部
4 懒汉模式:双重检查(推荐用) (两个if判断,对instance=null之后进行加锁,然后再判断null,再实例化)
public class SingletonTest6 {
private static SingletonTest6 instance = null;
private SingletonTest6() {}
public static SingletonTest6 getInstance() {
if(instance == null) {
synchronized (SingletonTest6.class) {
if(instance == null) {
instance=new SingletonTest6();
}
}
}
return instance;
}
}
y:集合以上几种的优点 线程安全,延迟加载,效率高
三:其他单例模式
1 直接使用静态内部类(推荐使用)
public class SingletonTest7 {
private SingletonTest7() {}
private static class SingletonTest7Instance{
private static final SingletonTest7 instance = new SingletonTest7();
}
public static SingletonTest7 getInstance() {
return SingletonTest7Instance.instance;
}
}
类加载的时候就会加载静态方法,直接实例化
2 枚举(其实就是静态常量)
public enum SingletonTest8 {
INSTANCE;
/**
* 其他方法
*/
public void test() {
System.out.println("普通方法");
}
}
--INSTANCE就是静态常量(是public final static 修饰的成员变量),只会创建一次,还实现了线程安全(但是自己不常用,听说大佬用);
四、使用场景:
1、要求生产唯一序列号。
2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
4、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
5、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
二十三种设计模式之单例模式完成!