单例模式
实现方式
- 饿汉式(静态常量)
public class Singleton{
//1.私有化构造函数,防止new对象
private Singleton(){}
//2.创建单例对象
private static final Singleton INSTANCE = new Singleton();
//3.获取单例对象的静态方法
public static Singleton getInstance(){
return INSTANCE;
}
}
总结:简单方便,通过类装载机制解决线程安全问题。
缺点: 如果使用过程中没有使用到改单例对象,会导致内存资源浪费,起不到懒加载的作用。
推荐指数:☆ ☆ ☆
- 饿汉式(静态代码块)
public class Singleton{
//1.私有化构造函数,防止new对象
private Singleton(){}
//2.创建单例对象
private static final Singleton INSTANCE;
static{
INSTANCE = new Singleton();
}
//3.获取单例对象的静态方法
public static Singleton getInstance(){
return INSTANCE;
}
}
总结:和基于静态变量的方式相同。
推荐指数:☆ ☆ ☆
- 懒汉式(线程不安全)
public class Singleton{
//1.私有化构造函数,防止new对象
private Singleton(){}
//2.定义单例对象
private static Singleton INSTANCE;
//3.判断单例对象是否为空,为空则先创建然后返回对象
public static Singleton getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
总结:虽然解决了懒加载的问题,但是存在严重的线程安全问题
推荐指数:不推荐
- 懒汉式(线程安全,同步方法)
public class Singleton{
//1.私有化构造函数,防止new对象
private Singleton(){}
//2.定义单例对象
private static Singleton INSTANCE;
//3.判断单例对象是否为空,为空则先创建然后返回对象,通过synchronized保证线程安全
public synchronized static Singleton getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
总结:解决了懒加载的问题,避免了资源的使用浪费,但每次线程获取单例对象都需要加锁,性能底下
推荐指数:☆
- 懒汉式(线程不安全,同步代码块)
public class Singleton{
//1.私有化构造函数,防止new对象
private Singleton(){}
//2.定义单例对象
private static Singleton INSTANCE;
//3.判断单例对象是否为空,为空则先创建然后返回对象,通过synchronized同步代码块
public static Singleton getInstance(){
if(INSTANCE == null){
synchronized(Singleton.class){
INSTANCE = new Singleton();
}
}
return INSTANCE;
}
}
总结:有人提出,每次获取单例对象都需要加锁的优化方案,在创建对象的时候加锁,但是当多线程获取单例对象的时候,可能存在多个线程都进入if判断语句中,导致获取对象并非单例。
推荐指数:不推荐
- 双重检查
public class Singleton{
//1.私有化构造函数,防止new对象
private Singleton(){}
//2.定义单例对象,volatile作用是强制把线程内存中的数据刷回主存
private volatile static Singleton INSTANCE;
//3.判断单例对象是否为空,为空则先创建然后返回对象,通过synchronized同步代码块
public static Singleton getInstance(){
if(INSTANCE == null){
synchronized(Singleton.class){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
总结:通过双重检查保证了线程安全也做到了懒加载
推荐指数:☆ ☆ ☆
- 静态内部类
public class Singleton{
//1.私有化构造函数,防止new对象
private Singleton(){}
//2.定义静态内部类,静态内部类只有在真正使用到的时候才会装载,而类装载是线程安全的,避免了线程安全问题
private static class SingletonInstance{
public static final Singleton INSTANCE = new Singleton();
}
//3.获取单例对象
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
总结:通过静态内部类的装载机制实现了线程安全问题,而且静态内部类只有在真正调用getInstance()方法时才会真的加载静态内部类起到了懒加载的作用。
推荐指数:☆ ☆ ☆ ☆ ☆
- 枚举方式
public class TestEnum {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
instance.sayHello();
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance == instance2);//true
System.out.println(instance.hashCode() == instance2.hashCode());//true
}
}
enum Singleton {
INSTANCE;
public void sayHello(){
System.out.println("hello");
}
}
总结:枚举创建单例的方式被《Effective Java》的作者认为是最佳实现单例的方式,使用简洁,无偿提供了序列化机制。
推荐指数:☆ ☆ ☆ ☆ ☆
使用举例:
public class Main {
public static void main(String[] args) {
DBConnection con1 = DataSourceEnum.DATASOURCE.getConnection();
DBConnection con2 = DataSourceEnum.DATASOURCE.getConnection();
System.out.println(con1 == con2);
}
}
//枚举类,实现单例
enum DataSourceEnum {
DATASOURCE;
private DBConnection connection = null;
private DataSourceEnum() {
connection = new DBConnection();
}
public DBConnection getConnection() {
return connection;
}
}
class DBConnection {}
单例模式注意事项和细节说明
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须记住使用相应的获取对象的方法,而不是new
- 单例模式使用场景:需要频繁的进行创建和销毁的对象,创建对象时消耗过多或消耗资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如:数据源、session工厂)