设计模式之单例模式

单例模式:确保一个类只有一个实例,并且提供一个全局的访问点。


我们先看一下标准的懒汉方式。


public class SingletonOne {
	private static SingletonOne Sing;
        private SingletonOne(){}
        public static SingletonOne getInstance(){
		if(Sing==null){
			Sing=new SingletonOne();
		}
		return Sing;
	}
}

分析一下这种写法。


首先声明了一个私有的静态变量 


然后我们可以在外部调用

SingletonOne.getInstance()获取对象实例。

但是现在我们模拟一下场景,如果同时两个或者更多线程同时去调用

SingletonOne.getInstance()

会出现什么情况呢,假设线程一先判断完Sing是否为null,这时候JVM把CPU资源切换到了第二个线程,第二个线程去再去判断,发现还是为null,那么将会创建两个不同的对象。如果在确保只有一个线程访问的情况下,这种方式是可以的


我们举个例子:


public class Test {
	public static void main(String[] args) {
		for (int i = 0; i < 50; i++) {
			Thread th=new Thread(new NewSingleton());
			th.start();
		}
	}
}

class NewSingleton implements Runnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(SingletonOne.getInstance());
	}
}

这时候我创建了50个线程 同时去调用SingletonOne.getInstance() 我们看一下打印结果。


com.wy.singleton.SingletonOne@22b5ecf4
com.wy.singleton.SingletonOne@2d3375e
com.wy.singleton.SingletonOne@2d3375e
com.wy.singleton.SingletonOne@22b5ecf4
com.wy.singleton.SingletonOne@2d3375e
com.wy.singleton.SingletonOne@2d3375e
com.wy.singleton.SingletonOne@2d3375e

....

这里我只列举几条 我们发现 获取的对象是不一样的。


所以这种方式不适合多线程的情况。


那么有人会说了 使用synchronized 同步方法 或者同步代码块不就可以解决这个问题吗  好 我们实验一下。


我们改动一下getInstance方法  


public synchronized static SingletonOne getInstance(){
		if(Sing==null){
			Sing=new SingletonOne();
		}
		return Sing;
	}


(在静态方法中使用synchronized ,默认锁为类的类 相当于SingletonOne.class)


然后我们再次运行一下 结果如下


com.wy.singleton.SingletonOne@a2c7b1
com.wy.singleton.SingletonOne@a2c7b1
com.wy.singleton.SingletonOne@a2c7b1
com.wy.singleton.SingletonOne@a2c7b1
com.wy.singleton.SingletonOne@a2c7b1
com.wy.singleton.SingletonOne@a2c7b1

发现这个获取的是一致的。


但是使用synchronized会造成比较耗时,确保只有一个线程进入此方法。 我们同时开启50个线程 那么他会一个个进入。


绝大部分的耗时操作都用在synchronized修饰符的同步准备上,因此从性能上说很不划算。


public class SingletonOne {
	private static SingletonOne Sing;
	private SingletonOne(){}
	public  static SingletonOne getInstance(){
		synchronized (SingletonOne.class) {
			if(Sing==null){
				Sing=new SingletonOne();
			}
			return Sing;
		}
	}
}

这种方式与上面那个方式类示 每次调用getInstance()还是要进行同步的。


我们再改一下


public class SingletonOne {
	private static SingletonOne Sing;
        private SingletonOne(){}
	public static SingletonOne getInstance() {
		if (Sing == null) {   
			synchronized (SingletonOne.class) {//第二个线程会在这里等待锁 第二个线程已经判断Sing==null 那么第二个线程会再次new一次的
				Sing = new SingletonOne();//假设第一个线程走到这里 
			}
		}
		return Sing;
	}
}


我们再看标准的饿汉方式。


根据JLS(Java Language Specification)中的规定 ,一个类在一个ClassLoader中只会被初始化一次,这点是JVM本身保证的,那就把初始化实例的事情扔给JVM好了,代码被改成这样:也就是标准的饿汉式写法


public class SingletonOne {
	private static SingletonOne Sing=new SingletonOne();;
        private SingletonOne(){}
	public static SingletonOne getInstance() {
		return Sing;
	}
}


我们再看一种写法


public class SingletonOne {
	private static class SingleHolder{
		static final SingletonOne sing=new SingletonOne();
	}
	private SingletonOne(){}
	public static SingletonOne getInstance(){
		return SingleHolder.sing;
	}
}

这两种方式都是标准的线程安全的写法。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值