一、饿汉式
1、静态常量
package singleton.type1;
public class TestSingleton01 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode = " + instance1.hashCode());
System.out.println("instance2.hashCode = " + instance2.hashCode());
}
}
class Singleton {
//1.构造器私有化,不能通过new创建实例
private Singleton(){
}
//2.类加载时就初始化实例
private static final Singleton INSTANCE = new Singleton();
//3.提供获取实例的静态方法
public static Singleton getInstance(){
return INSTANCE;
}
}
2、静态代码块
package singleton.type2;
public class TestSingleton02 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode = " + instance1.hashCode());
System.out.println("instance2.hashCode = " + instance2.hashCode());
}
}
class Singleton {
//1.构造器私有化
private Singleton(){
}
//2.静态实例对象
private static Singleton instance;
//3.通过静态代码块实例化对象
static {
instance = new Singleton();
}
//4.提供静态的获取实例的方法
public static Singleton getInstance(){
return instance;
}
}
结论:饿汉式虽然实现了单例,不存在线程安全问题,但因为在类加载时就创建了实例,如果在后续代码中没有使用到这个实例,可能造成内存浪费。
二、懒汉式
1、懒汉式写法1(存在线程安全问题)
package singleton.type3;
public class TestSingleton03 {
public static void main(String[] args) {
//模拟多线程下线程不安全问题
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
}
}
class Singleton {
//1.构造器私有化
private Singleton(){
}
//2.静态实例对象
private static Singleton instance;
//3.在调用时实例化对象
public static Singleton getInstance(){
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
执行n次后出现线程安全问题
说明:
1)起到了Lazy Loading的效果,但是只能在单线程下使用。
2)在多线程下使用,会有线程安全问题。
3)线程安全问题如下:
当线程A进行if(instance==null),判断为null进入if后还没有实例化对象,此时线程B也进行了if(instance==null),判断null进入if,结果线程A和线程B创建了两个不同的实例。
结论:实际开发中不推荐使用。
2、懒汉式写法2(同步方法,线程安全)
package singleton.type4;
public class TestSingleton04 {
public static void main(String[] args) {
//模拟多线程下线程不安全问题
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
//其他线程复制上面的线程即可
}
}
class Singleton {
//1.构造器私有化
private Singleton(){
}
//2.静态实例对象
private static Singleton instance;
//3.在调用时实例化对象,同步方法
public static synchronized Singleton getInstance(){
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
说明:
1)解决了线程安全问题
2)效率低
因为加了同步锁,当有线程进入方法获取实例时,其他线程都要在外面等待。而实际上是只要判断instance为null时才需要创建实例,instance不为null时,只需要return就行了。
结论:在实际开发中,不推荐使用。
3、懒汉式写法3(同步代码块,双重检查,线程安全)
package singleton.type5;
public class TestSingleton05 {
public static void main(String[] args) {
//模拟多线程下线程不安全问题
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
//其他线程复制上面的线程即可
}
}
class Singleton {
//1.构造器私有化
private Singleton(){
}
//2.静态实例对象
private static Singleton instance;
//3.在调用时实例化对象
public static Singleton getInstance(){
if(instance == null) {
//4.同步代码块
synchronized(Singleton.class){
//5.双重检查
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
说明:
1)解决了线程安全问题,提高了效率。
2)提高了效率
当线程A第一次判断instance为null时,进入if,再次判断instance为null,进入if,此时还没有创建实例,线程B第一次判断instance为null,进入if,由于线程A已经进入,加锁了,无法进行第二次if判断,此时线程A创建实例结束,线程B进入第二次if判断instance不为null,直接返回。当后续有线程获取实例时,只需要进行第一次if判断,instance不为null,就直接返回,提高了效率。
结论:实现Lazy Loading ,线程安全,效率高,实际开发中推荐使用。
三、静态内部类实现单例(线程安全,效率高)
package singleton.type6;
public class TestSingleton06 {
public static void main(String[] args) {
//模拟多线程下线程不安全问题
new Thread(){
public void run() {
Singleton instance = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
};
}.start();
//其他线程复制上面的线程即可
}
}
class Singleton {
//1.构造器私有化
private Singleton(){
}
//2.静态内部类
private static class Instance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return Instance.INSTANCE;
}
}
说明:
1)延迟加载
静态内部类并不会随着主类的加载而加载,而是在调用时才会加载,所以实现了LazyLoading。
2)线程安全
类的静态属性只会在类第一次加载时初始化,在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程时无法进入的。
结论:线程安全,延迟加载,效率高,实际开发中推荐使用。
四、枚举实现单例
package singleton.type7;
public class TestSingleton07 {
public static void main(String[] args) {
Singleton instance1 = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode = " + instance1.hashCode());
System.out.println("instance2.hashCode = " + instance2.hashCode());
}
}
enum Singleton {
INSTANCE;
}