单例模式介绍
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式确保一个类最多只有一个实例,并提供一个全局访问点。
优点:
1.在内存中只有一个对象,节省内存空间;
2.避免频繁的创建销毁对象,可以提高性能;
3.避免对共享资源的多重占用,简化访问;
4.为整个系统提供一个全局访问点。
缺点:
1.不适用于变化频繁的对象;
2.存在线程不安全的问题;
3.如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;
代码实现
单例模式可分为,饿汉式,懒汉式,还有一种双锁模式
饿汉式具体实现
饿汉式,顾名思义就是在单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用。这种方法是线程安全的,不过要是加载的对象没有被使用就会造成资源的浪费。
public class PreloadSingleton {
public static PreloadSingleton instance = new PreloadSingleton();
//私有化构造方法
private PreloadSingleton() {
};
public static PreloadSingleton getInstance() {
return instance;
}
}
懒汉式具体实现
为了避免内存的浪费,我们可以采用懒汉式的方法,即用到该单例对象的时候再创建。这样可以有效的避免资源被浪费的问题,不过会带来线程安全问题。
public class Singleton {
private static Singleton instance=null;
private Singleton(){
};
public static Singleton getInstance()
{
if(instance==null)
{
instance=new Singleton();
}
return instance;
}
}
为什么这种方法无法保证线程安全?
原因就在于不满足原子性或者顺序性。创建一个对象分为3步:初始化内存空间,初始化对象,分配的内存地址。jvm为了提高程序执行性能,会对没有依赖关系的代码进行重排序,上面2和3行代码可能被重新排序。即:可能先分配地址,再初始化对象。
假设现在有两个线程,都使用这种方式创建对象。线程A先分配了内存地址,但是没有初始化对象。线程B在这时检查到内存地址不为null,线程B接下来将访问instance引用的对象。也就是说线程B将会访问到一个还未初始化的对象。造成空指针异常,线程不安全!
双锁单例模式
为了解决懒汉式线程不安全的问题,可以引入锁来解决.。在创建对象时加入锁,并且加入关键字volatile保证对象实例化过程的顺序性。
public class Singleton {
private volatile static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
使用双锁单例就成功的保证了单例模式的线程安全问题。
总结
饿汉式保证了线程安全但是可能会造成资源浪费。
懒汉式资源不会浪费但是会造成线程不安全的问题。