单例模式属于创造型设计模式
总的来说可以分为懒汉型和饿汉型单例模式,下面我将列举几种单例模式的写法并逐个分析
1.饿汉式单例模式(线程安全)
package com.wantao.concurrency.singleton;
/**
* 饿汉式单例模式1
*/
public class Singleton1 {
private static Singleton1 instance = new Singleton1();//类加载时就创建了对象
private Singleton1() {
}
public static Singleton1 getInstance() {
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton1.getInstance().hashCode());
System.out.println(Singleton1.getInstance().hashCode());
}
}
分析:这是饿汉型单例模式的典型的写法,因为它在类加载时就创建了对象,所以这种方式是属于线程安全的。
2.饿汉型单例模式(线程安全)
package com.wantao.concurrency.singleton;
/**
* 饿汉式单例模式2
*/
public class Singleton2 {
private static Singleton2 instance = null;
static {
instance = new Singleton2();
}
private Singleton2() {
}
public static Singleton2 getInstance() {
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton2.getInstance().hashCode());
System.out.println(Singleton2.getInstance().hashCode());
}
}
分析:这也是饿汉式单例模式,与上种写法相比,只是把创建对象的过程放在了static代码块中.但要注意
3.懒汉型单例模式(线程不安全)
package com.wantao.concurrency.singleton;
/**
* 懒汉式单例模式1
*/
public class Singleton3 {
private Singleton3() {
}
private static Singleton3 instance = null;
public static Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton3.getInstance().hashCode());
System.out.println(Singleton3.getInstance().hashCode());
}
}
分析:这是最简单的懒汉式单例模式,它在第一次访问时才创建实例。但这种写法是线程不安全的。
假设有A,B两个线程同时调用getInstance()方法进行创建,此时instance都为空,两个线程都会new新的对象.
4.懒汉型单例模式(线程安全但是不推荐)
package com.wantao.concurrency.singleton;
/**
* 懒汉式单例模式2
*/
public class Singleton4 {
private Singleton4() {
}
private static Singleton4 instance = null;
public static synchronized Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton4.getInstance().hashCode());
System.out.println(Singleton4.getInstance().hashCode());
}
}
分析:使用synchronized同步方法,线程安全,但是每次只能一个线程获得对象,效率太低.
5.懒汉型单例模式(线程安全但是不推荐)
package com.wantao.concurrency.singleton;
/**
* 懒汉式单例模式3
*/
public class Singleton5 {
private Singleton5() {
}
private static Singleton5 instance = null;
public static Singleton5 getInstance() {
synchronized (Singleton5.class) {
if (instance == null) {
instance = new Singleton5();
}
}
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton5.getInstance().hashCode());
System.out.println(Singleton5.getInstance().hashCode());
}
}
分析:这种写法同上,只不过synchronized修饰的不是方法而是代码块.
6.懒汉型单例模式(线程不安全)
package com.wantao.concurrency.singleton;
/**
* 懒汉单例模式4
*/
public class Singleton6 {
private Singleton6(){
}
private static Singleton6 instance=null;
public static Singleton6 getInstance(){
if(instance==null){
synchronized (Singleton6.class){
if(instance==null){
instance=new Singleton6();
}
}
}
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton6.getInstance().hashCode());
System.out.println(Singleton6.getInstance().hashCode());
}
}
分析:使用双重检测加synchronized(double checked locking)实现单例模式,看上去是线程安全的,但是由于jvm的指令重排,这也是不安全的.
新建对象一般三个步骤:
1.在堆上面分配了内存空间
2.初始化对象
3.将对象的引用指向刚刚分配的内存空间。
但经过jvm的指令重排后顺序可能变为了:132
这样如果有A,B两个线程,当A线程完成了1,3两个步骤时,B线程这时候也获取对象,获取的是未初始化的对象.
7.懒汉型单例模式(线程安全,推荐使用)
package com.wantao.concurrency.singleton;
/**
* 懒汉单例模式5
*/
public class Singleton7 {
private Singleton7(){
}
private static volatile Singleton7 instance=null;
public static Singleton7 getInstance(){
if(instance==null){
synchronized (Singleton7.class){
if(instance==null){
instance=new Singleton7();
}
}
}
return instance;
}
public static void main(String[] args) {
System.out.println(Singleton7.getInstance().hashCode());
System.out.println(Singleton7.getInstance().hashCode());
}
}
分析:与上面的方法相比,使用了volatile关键字禁止了指令重排,使线程变得安全.
8.懒汉型单例模式(线程安全,推荐使用)
package com.wantao.concurrency.singleton;
/**
* 懒汉单例模式6
*/
public class Singleton8 {
private Singleton8(){
}
public static Singleton8 getInstance(){
return Singleton.INSTANCE.getInstance();
}
private enum Singleton{
INSTANCE;
private Singleton8 instance;
Singleton(){
instance=new Singleton8();
}
public Singleton8 getInstance(){
return instance;
}
}
public static void main(String[] args) {
System.out.println(Singleton8.getInstance().hashCode());
System.out.println(Singleton8.getInstance().hashCode());
}
}
分析:使用enum(枚举)的方式,默认就是线层安全的,推荐使用
饿汉式与懒汉式单例模式对比:
1.饿汉式在类加载时就创建了实例,本身就是线程安全的,而懒汉式则在第一次调用时才创建实例,需要加锁或者enum来实现线程安全.
2.饿汉式单例模式没有加锁所以效率高,但是如果一开始就实例化了但是最后没调用就浪费内存。
转载请注明文章地址:https://blog.csdn.net/niangou0915/article/details/90316476