书上对单例模式的定义:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象,一个最好的办法就是,让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
这里我想跟大家说下我在书上看到一句话,当我们在构建软件的世界中遇到问题时,我们从现实生活中寻找答案,以及现实生活中很多的可行解,都能从软件的世界中得到启发。大家共勉。
首先防止new 把类的构造方法声明为private, 这样外部就不能通过new来构建新的实例了。其次提供一个全局访问点,即声明一个用static修饰获取的方法来访问。
第一种单例,这种方法又叫做懒汉式,线程不安全的。
public class Singleton1 {
private static Singleton1 instance;
private Singleton1(){
}
public static Singleton1 GetInstance(){
if(instance == null){
instance = new Singleton1();
}
return instance;
}
}
测试
public static void main(String[] args) {
Singleton1 singleton1 = Singleton1.GetInstance();
Singleton1 sin2 = Singleton1.GetInstance();
System.out.println(singleton1 == sin2);
}
结果:
true
第二种,对第一种方法进行改进,使让其线程安全 于是进行加锁,这样就防止多个线程越过if==null的情况,再多次创建线程。
public class Singleton2 {
private static final Object obj = new Object();
private static Singleton2 instance;
private Singleton2(){
}
public static Singleton2 GetInstance(){
synchronized (obj){
if(instance == null){
instance = new Singleton2();
}
}
return instance;
}
}
测试:
public class Main extends Thread{
@Override
public void run() {
System.out.println(Singleton2.GetInstance().hashCode());
}
public static void main(String[] args) {
/* Singleton1 singleton1 = Singleton1.GetInstance();
Singleton1 sin2 = Singleton1.GetInstance();
System.out.println(singleton1 == sin2);
*/
for(int i = 0;i<6;i++){
Thread myThread = new Main();
myThread.start();
}
}
}
结果:
2124340618
2124340618
2124340618
2124340618
2124340618
2124340618 //代表线程安全,实现了单例
上面这种性能降低了。为了增加点性能,于是将锁的粒度精细,但是,要预防死锁。
第三种: 采用网上的双重检测判空 但是这个不一定保证对,因为不是原子操作。所以多个线程操作第一判断非null时可能会直接返回一个还未分配完内存的地址。解决办法是给变量加上volatile.
public class Singleton3 {
private static Singleton3 instance;
private static final Object obj = new Object();
private Singleton3(){
}
public static Singleton3 GetInstance(){
if(instance == null){
synchronized (obj){
if(instance == null){
instance = new Singleton3();
}
}
}
return instance;
}
}
测试:测试代码和上面一样
1372676747
1372676747
1372676747
1372676747
1372676747
1372676747
第四种,推荐网上的静态内部类的方法
public class Singleton4 {
private static Singleton4 instance;
private Singleton4(){
}
private static class SingletonInner{
private static final Singleton4 INSTANCE = new Singleton4();
}
public static final Singleton4 getInstance(){
return SingletonInner.INSTANCE;
}
}
测试代码不附上了。
同时为什么这个模式能保证延迟初始化,又能保证单例,保证线程安全,根据虚拟机规范来保证。请参考:
https://blog.csdn.net/mnb65482/article/details/80458571
还有一种方法,即通过类的初始化来保证单例
public class Singleton5 {
private static Singleton5 instance = null;
private Singleton5(){}
static{
instance = new Singleton5();
}
public static Singleton5 getInstance() {
return instance;
}
}
最后一种就是枚举来实现
枚举实现,每个实例默认都是static final的.
public class Single {
private enum EnumSingleton {
INSTACE;
private Single single;
private EnumSingleton() {
this.single = new Single();
}
private Single getInstance() {
return single;
}
}
public static Single getInstance() {
return EnumSingleton.INSTACE.getInstance();
}
public static void main(String[] args) {
Single a = EnumSingleton.INSTACE.getInstance();
Single b = EnumSingleton.INSTACE.getInstance();
System.out.println("a==b"+(a == b));
}
结果:a==btrue