单例模式
单例模式可以有8种实现方式
1.饿汉式(静态常量)
步骤:
- 构造器私有化
- 类的内部创建对象
- 向外提供一个静态的公共方法getInstance
- 代码实现
public class Singleton1 {
// 2. 类的内部创建对象
private final static Singleton1 INSTANCE=new Singleton1();
//1.构造器私有化 外部就不能通过new来创建对象
private Singleton1() {
}
//3.向外提供一个public 静态方法 返回实例对象
public static Singleton1 getInstance() {
return INSTANCE;
}
}
- 优点:
1. 优点比较简单,在类装载的时候完成了实例化,避免了线程同步问题 - 缺点:
1. 在类装载的时候就完成了实例化,没有懒加载的效果,因为不管用还是不用实例,都存在,则会造成内存的浪费。
2. 可能造成内存浪费
2.饿汉式(静态代码块)
步骤:
- 构造器私有化
- 类的内部创建对象(在静态代码块中创建单例对象)
- 向外提供一个静态的公共方法getInstance
- 代码实现:
public class Singleton2 {
private static Singleton2 instance;// 2. 类的内部创建对象
static {//在静态代码块中创建单例对象
instance=new Singleton2();
}
private Singleton2() {//1.构造器私有化 外部就不能通过new来创建对象
}
//3.向外提供一个public 静态方法 返回实例对象
public static Singleton2 getInstance() {
return instance;
}
}
- 优点:
1. 简单,类装载的时候,就执行静态代码块,执行静态代码块,初始化类的实例。 - 缺点:
1. 可能会造成内存浪费
3.懒汉式(线程不安全)
步骤:
- 构造器私有化
- 类的内部对象
- 向外提供一个静态的公共方法getInstance向外提供一个public的修饰的方法,当调用时才会去生成对象
- 代码实现:
public class Singleton3 {
private static Singleton3 instance;// 2. 类的内部创建对象
private Singleton3() {// 1.构造器私有化 外部就不能通过new来创建对象
}
// 3.向外提供一个public的修饰的方法,当调用时才会去生成对象
public static Singleton3 getInstance() {
//在调用方式 说明在使用对象时才回去创建对象
if (instance == null) {//保证单例 如果对象创建就返回 如果没有创建就创建
instance = new Singleton3();
}
return instance;
}
}
- 优缺点:
1. 起到的lazy loading的效果,但是只能在单线程下才使用
2. 如果在多线程,当一个线程进入if(singltton==null)判断语句块,还在执行的同时。另一个线程也通过判断语句判断是否,有可能会生成对个实例。
3. 在实际运用中,很少用。
4.懒汉式(-线程安全,同步方法)
- 在
getInstance()
方法中加入synchronized
来修饰
private static Singleton4 instance;// 2. 类的内部创建对象
private Singleton4() {// 1.构造器私有化 外部就不能通过new来创建对象
}
//加入了synchronized解决线程不安全的问题
// 3.向外提供一个public的修饰的方法,当调用时才会去生成对象
public static synchronized Singleton4 getInstance() {
//在调用方式 说明在使用对象时才回去创建对象
if (instance == null) {//保证单例 如果对象创建就返回 如果没有创建就创建
instance = new Singleton4();
}
return instance;
}
- 优缺点:
1. 解决了线程不安全的问题
2. 效率很低,每个想要获得类的实例时,执行getInstance()都要进行同步,而其实这个方法只执行一次实例化就行,后面的线程如果想获得实例,就通过return的方式来获得
3. 实际中,效率很低
5.懒汉式(线程安全,同步代码块)
缺点:没有办法解决线程并不安全问题,不能使用。
6. 双重检查(比较推荐)
- 使用
volatile
来修饰关键字
private static volatile Singleton6 instance;// 2. 类的内部创建对象
private Singleton6() {// 1.构造器私有化 外部就不能通过new来创建对象
}
//双重检查的办法 解决线程安全的问题 以及同步问题,懒加载问题 ,同时保证了效率
public static Singleton6 getInstance() {
if(instance==null) {
synchronized (Singleton6.class) {
if(instance==null) {
instance=new Singleton6();
}
}
}
return instance;
}
7. 静态内部类(推荐使用)
- 静态内部类特点:1. 类装载时,静态的内部类不会被立即装载;2.通过调用getInsatnce()时,内部类是通过类装载来完成内部类的装载,是线程安全的
// private static volatile Singleton7 instance;// 2. 类的内部创建对象
private Singleton7() {// 1.构造器私有化 外部就不能通过new来创建对象
}
//2.静态内部类 该类有一个静态属性 类型为Singleton7
private static class SingletonInstance{//在Singleton7进行类装载的时候,并不会马上执行,不会导致静态内部类的马上装载,
//从而保证了懒加载。
private static final Singleton7 INSTANCE=new Singleton7();
}
//3.提供一个public的方法 来获取实例,直接返回
public static synchronized Singleton7 getInstance() {//在执行方法时静态内部类会进行装载SingletonInstance,在装载时,是通过类装载来完成的,所以线程安全的
return SingletonInstance.INSTANCE;
}
- 优缺点:
1. 采用类装载的机制来保证初始化实例只有一个个线程
2. 静态内部类在singleton类被装载时,并不会立即实例化,而是在调用getInstance才会装载内部类,从而实例化
3. 类的静态属性,只会在第一次加载类的时候初始化,JVM帮助保证了线程的安全性,在类进行初始化时,别的线程不会进来。
4. 避免的线程的不安全,利用内部类特点延迟加载,效率高;
8. 枚举(推荐使用)
- 借助JDK1.5中添加枚举来实现单例模式,不仅能避免多线程同步问题,还能方式反序列化重新创建对象
//使用枚举来实现单例模式
enum Singleton {
INSTANCE;//枚举中只有一个属性 来保证它的单例
// public void say() {
// System.out.println("la");
// }
}
public class Singleton8{
public static void main(String[] args) {
Singleton singleton1=Singleton.INSTANCE;
Singleton singleton2=Singleton.INSTANCE;
System.out.println(singleton1==singleton2);
}
}
单例模式JDK分析:
Runtime
详细代码:
public class Runtime {
//饿汉式单例模式的写法
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
}
单例模式的注意事项
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些要频繁创建销毁的对象时,使用单例单例模式可以提高系统的性能
- 当想创建一个单例类时,只能使用相应的获取对象对象的方法,而不是通过new
- 单例模式的使用场景:需要频繁的进行创建销毁对象,创建对象时消耗过多资源,但经常用到的对象,工具类对象,频繁访问的对象,或者文件的对象(数据源,session工厂等)