1. 单例模式有哪些特点:
1.1 不允许 new , 保证唯一性
1.2 对外提供获取对象唯一实例的接口方法
2.实现方式
2.1 简单 单例模式 , 线程安全(饿汉式)
public class Test{
// 定义一个私有的构造方法,阻止外部 new 这个对象实现实例化
private Test() { }
// 将自身的实例对象设置为一个属性,并加上Static和final修饰符
private static final Test test= new Test();
// 静态方法返回该类的实例
public static Test getTest() {
return test;
}
}
2.2 如2.1所示的单例模式,如果有100000这样的单例在部署发布后就有100000个对象已经被实例化了,这种情况占了服务器资源,优化后如下所示:(饱汉式,线程不安全)
public class Test{
// 定义私有构造方法
private Test() { }
// 不初始化,注意这里没有使用final关键字
private static Test test;
// 调用时再初始化Test,但是多线程访问时,可能造成重复初始化问题
public static Test getTest() {
if (test== null) test= new Test();
return test;
}
}
2.3 如2.2所示例子会出现线程造成重复初始化问题,优化如下:(饱汉式,线程安全的简单实现)
public class Test{
// 防止通过 new 去实例化
private Test() { }
// 不初始化,注意这里没有使用final关键字
private static Test test;
// 使用synchronized 避免多线程访问时,可能造成重的复初始化问题
public static synchronized Test getTest() {
if (test == null)
test = new Test();
return test;
}
}
2.4 如2.3所示例子已经达到了单例模式的标准,但出于性能考虑,还需要继续优化,代码如下:(饱汉式,线程安全 并且效率高 单例模式最优方案)
public class Test {
// 定义一个私有构造方法
private Test () { }
//使用volatile保证了多线程访问时test变量的可见性,避免了test初始化时其他变量属性还没赋值完时,被另外线程调用
private static volatile Test test;
//定义一个共有的静态方法,返回该类型实例
public static Test getTest () {
// 不使用同步代码块,test不等于null时,直接返回对象,提高运行效率
if (test== null) {
// 对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不再重复被创建
synchronized (Test.class) {
//未初始化,则初始instance变量
if (test== null) {
test= new Test ();
}
}
}
return test;
}
}
2.5 静态内部类方式
public class Test{
private static class TestHolder {
private static final Test TEST = new Test ();
}
private Test(){}
public static final Test getTest() {
return TestHolder.TEST ;
}
}
// 这种方式同样利用了classloder的机制来保证初始化 test 时只有一个线程
// 它跟第三种和第四种方式不同的是:
// 第三种和第四种方式是只要Test类被装载了,那么test就会被实例化(没有达到lazy loading效果)
// 而这种方式是Test类被装载了,test不一定被初始化。
目前最为安全的实现单例的方法是通过内部静态enum的方法来实现,因为JVM会保证enum不能被反射并且构造器方法只执行一次。如下:
2.6 使用枚举的单例模式
public class EnumSingleton{
private EnumSingleton(){}
public static EnumSingleton getInstance(){
return Singleton.INSTANCE.getInstance();
}
private static enum Singleton{
INSTANCE;
private EnumSingleton singleton;
//JVM会保证此方法绝对只调用一次
private Singleton(){
singleton = new EnumSingleton();
}
public EnumSingleton getInstance(){
return singleton;
}
}
}
2.7 使用枚举,static处调用,初始化一次
public class StaticInitTest {
public static List<Integer> dataList = null;
static{
dataList = Singleton.INSTANCE.init();
}
private static enum Singleton {
INSTANCE;
private List<Integer> list;
private Singleton(){
fillData();
}
// 初始化数据
private void fillData(){
list = new ArrayList<Integer>(5);
for(int i =1; i<6; i++){
list.add(i);
}
}
// 初始化入口
public List<Integer> init(){
return list;
}
}
}