单例模式只是像外界提供一个对象,它在自己内部创建一个自己的对象并且提供一个方法让外界来得到,从而保证程序中只有一个自己的对象。
第一种:饿汉式
public class Single {
private static final Single s = new Single(); //创建仅有的一个对象
//构造函数必须私有化
private Single(){
}
//向外界提供一个得到该对象的方法
public static Single getInstance(){
return s;
}
}
private static final Single s = new Single();
这里的final写与不写都可以,但是写上的话会更严谨,所以还是写上好
第二种:懒汉式(延迟加载)
public class Single {
private static Single s = null;
private Single(){
}
public static Single getInstance(){
if(s==null){
s = new Single();
}
return s;
}
}
该写法延迟了对象的创建,更加节约空间,因为在调用getInstance方法时才创建对象。
这里要注意
private static (这里不能像饿汉式那样写final,因为是延迟加载,如果写了final关键字那么s就一直为null了)Single s = null
懒汉式还有一个多线程访问时的安全问题
public static Single getInstance(){
if(s==null){
==》A线程
==》B线程
s = new Single();
}
return s;
}
如果A线程进来后走到箭头指的位置后cpu切换到了B线程,那么A就会停在那个位置然后B线程开始走,B线程再停在那里A线程醒了就会出现这两个线程都进入到if的判断了,于是会创建两次对象,而单例只能有一个对象,所以要让给方法进行同步处理
public static synchronized Single getInstance(){
if(s==null){
s = new Single();
}
return s;
}
进行同步后就解决了问题
但是这里还有个效率的问题,因为加了同步后每个线程都要判断锁会很慢,所以应该优化一下
public static Single getInstance(){
if(s==null){
synchronized(Single.class){
if(s==null){
s = new Single();
}
}
}
return s;
}
上面多了一层判断,因为多个线程进来时都会判断一层外层的s是否为null,当对象没创建的时候就会继续判断锁,直到创建对象。然后其他的线程进来后直接判断第一层的s是否为null,因为已近创建了对象所以他就不会再去判断锁了也就提高了效率