*单例模式之饿汉模式
声明:(不使用反射机制)在非正常情况下初始化对象比如通过反射初始化,使用反射之后单例模式也就失去效果了。
饿汉实现
class Singletion{
//私有化构造器
private Singletion(){
}
//内部创建对象
private static Singletion s =new Singletion();
public static Singletion getSingletion(){
return s;
}
}
之所以在设计模式中要有单例模式是因为要确保在堆内存中始终只有一个对像实例
在以上代码中第一步就得私有化构造器就是为了防止有人在外部去实例化对象
饿汉饿汉顾名思义就可以理解直接去拿现成的来用,就很迫不及待!在里面我直接就创建好了对象,我只需要在外部通过Singletion.getSingletion()就可以直接拿到Singletion对象!并且也保证了对象的唯一性!
由于static 的存在,Singletion类在加载时候就被实例化了而且在内存中Class只会加载一次。所以不管多少个线程进来拿Singletion对象的时候始终只有一个Singletion对象!
so单例模式中的饿汉模式是天然线程安全的!
懒汉模式
下面展示一些 内联代码片
。
懒汉实现
class Singletion{
private Singletion(){
}
private static Singletion singletion=null;
public static Singletion getSingletion(){
if (singletion==null){
singletion= new Singletion();
}
return singletion;
}
}
测试
public class singletionTest1 {
public static void main(String[] args) {
new Thread(()->{
Singletion singletion = Singletion.getSingletion();
System.out.println(singletion);
}).start();
new Thread(()->{
Singletion singletion = Singletion.getSingletion();
System.out.println(singletion);
}).start();
}
}
同样第一步先得私有化构造器防止有人在外部去实例化对象
以上代码如果只有一个线程去调用的话那确实不会出现问题,如果出现多个线程去调用呢?可以看到在以上开启两个线程的时候就可以看出拿到的已经不再是同一个对象,而造成这种现象的问题就在于if判断的那个位置!
if (singletion==null){
//A
singletion= new Singletion();
}
当有个线程进入到A位置的时候如果会发生阻塞,而此时由于没有锁另一个线程又进来了并且实例化了Singletion对象,第二个线程出去的时候第一个进来的线程就没有判断了 就会又去实例化一次对象。所以就造成了 Singletion对象的两次赋值所以懒汉模式是不安全的!
解决他无非就是加锁嘛!这里我们采用同步代码块!
解决懒汉式的线程安全问题
// An highlighted block
public class singletionTest1 {
public static void main(String[] args) {
new Thread(()->{
System.out.println(Singletion.getSingletion());
}).start();
new Thread(()->{
System.out.println(Singletion.getSingletion());
}).start();
new Thread(()->{
System.out.println(Singletion.getSingletion());
}).start();
new Thread(()->{
System.out.println(Singletion.getSingletion());
}).start();
new Thread(()->{
System.out.println(Singletion.getSingletion());
}).start();
}
}
class Singletion {
private Singletion() {
}
private static Singletion singletion = null;
public static Singletion getSingletion() {
synchronized (Date.class) {
if (singletion == null) {
singletion = new Singletion();
}
}
return singletion;
}
}
可以看到加了同步代码块的时候就保证了对象的唯一性。就是说当一个线程进来之后获得Date锁 并且执行对象实例化操作的时候其他线程只能在外部等待知道进去的线程结束后释放掉锁他才可以进!进去之后就发现singletion 已经不为空了那么就直接返回singletion,从而保证对象的唯一性!那么以上代码还存在那些看不见的问题呢?言下之意在程序没有出错的情况下就只剩下性能问题了!
试想一下,在第一次被实例化后其他的线程是不是每一次都要拿锁(synchronized是一个重操作非常消耗性能)然后进去判断啊?那这样就会很慢!有没有一种方法让他不用去拿锁呢?很简单,就是说我们就只需要在同步代码块上在来判断一次纠结解决了性能问题。
再次优化
class Singletion {
private Singletion() {
}
private static Singletion singletion = null;
public static Singletion getSingletion() {
if (singletion == null) {
synchronized (Date.class) {
if (singletion == null) {
singletion = new Singletion();
}
}
}
return singletion;
}
}
在外部在加一次判断,在第一个线程把singletion 实例化后singletion 就有值了!其他线程判断有值直接返回!就不用再执行下面的代码!!!!!!