
- 一、单例模式?
- 单例模式概述
- 特点
- 1.简单单例模式
- 2.饿汉式
- 3.懒汉式
- 4.静态内部类
特点单例对象的类只能允许一个实例存在
单例模式下只会有一个对象进行创建
1.简单单例模式类构造器私有,持有自己类的引用,对外提供获取实例的静态方法
下方所示代码为最简单单例模式
public class Singleton {
//持有自己类的引用
private static final Singleton INSTANCE = new Singleton();
//构造器私有
private Singleton() {
}
//对外提供获取实例的静态方法
public static Singleton getInstance(){
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(getInstance().hashCode());
System.out.println(getInstance().hashCode());
}
}
2.饿汉式执行以上代码所得结果如下图,由此可见,在单例模式下,只会创建一个对象
饿汉式为单例模式的一种著名的实现方式,即在实例在初始化的时候就已经建好,代码如下
public class HungrySingleton {
//持有自己类的引用
private static final HungrySingleton INSTANCE;
//静态代码块只在加载该类时执行一次
static {
INSTANCE = new HungrySingleton();
}
//类构造器私有
private HungrySingleton() {
}
//对外提供获取实例的静态方法
public static HungrySingleton getInstance(){
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(getInstance().hashCode());
System.out.println(getInstance().hashCode());
}
}
执行结果如图
3.懒汉式懒汉式的好处在于没有线程安全问题,但会存在资源浪费的问题,一开始就实例化对象,如果对象最后没有被使用,资源就会被浪费
懒汉式及在用到实例的时候先去判断有没有,如果有就返回他,没有就创建,相较于饿汉式,懒汉式更加节省资源,代码如下
public class LazySingleton {
//持有自己类的引用
private static LazySingleton INSTANCE;
//构造器私有
private LazySingleton() {
}
//对外提供获取实例的静态方法
public static LazySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazySingleton();
}
return INSTANCE;
}
}
仔细阅读上面的代码,其实是存在一些问题的。当有多个线程并发调用getInstance()时,上面的代码可能会不满足“单例”。
下面我们可以模拟一下多个线程去调用getInstance()时的场景,下面代码是创建10个线程去调用getInstance()方法
for (int i = 0; i < 10; i++) {
new Thread(() -> System.out.println(getInstance().hashCode())).start();
}
结果如图:可以看到此处创建了两个实例
为什么会发生这种情况呢?这是因为在并发情况下,当多个线程同时执行到if (INSTANCE == null)时都会判断为true,从而创建多个INSTANCE对象。要解决这个问题也很简单,可以在方法上加上synchronized关键字,这样就可以保证只有一个线程去创建INSTANCE对象。
这样虽然保证了线程安全,但synchronized会影响到getInstance()方法的性能,于是我们可以使用双重检查锁,判断INSTANCE为空的时候再加锁,代码如下
/**
* 懒汉式,双重检查锁
* */
public class LazySingleton2 {
//持有自己类的引用
private static LazySingleton2 INSTANCE;
//构造器私有
private LazySingleton2() {
}
//对外提供获取实例的静态方法
public static LazySingleton2 getInstance(){
if (INSTANCE==null){
synchronized (LazySingleton2.class){
if (INSTANCE==null){
INSTANCE=new LazySingleton2();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(getInstance().hashCode());
System.out.println(getInstance().hashCode());
}
}
引入双重检查锁就可以保证性能。网上查阅资料显示,双重检查锁并不能保证线程安全,这是因为jvm的指令重排,这块暂未了解,待学习后再更新。对其线程不安全的解决方法是使用volatile关键字,如下:
/**
* 懒汉式,双重检查锁
* */
public class LazySingleton2 {
//持有自己类的引用
//private static LazySingleton2 INSTANCE;
private volatile static LazySingleton2 INSTANCE;
//构造器私有
private LazySingleton2() {
}
//对外提供获取实例的静态方法
public static LazySingleton2 getInstance(){
if (INSTANCE==null){
synchronized (LazySingleton2.class){
if (INSTANCE==null){
INSTANCE=new LazySingleton2();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(getInstance().hashCode())).start();
}
}
}
4.静态内部类
不学了,开摆
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)