赞
踩
私有构造函数,禁止外部访问。
使用static和final做到类加载到内存后,就实例化一个单例,而且是唯一的,JVM保证线程安全。简单实用。
唯一缺点:不管用到与否,类装载时就完成实例化,占用内存。
package com.DesignPattern.singleton; /* 饿汉式 类加载到内存后,就实例化一个单例,JVM保证线程安全 简单实用 唯一缺点:不管用到与否,类装载时就完成实例化 */ public class Mgr01 { private static final Mgr01 INSTANCE = new Mgr01();//类加载时(static)就进行对象实例化(final),是唯一的 private Mgr01(){};//私有构造函数,禁止外部访问 // 被static关键字修饰的变量和方法统一属于类的静态资源,是类实例之间共享的。被static关键字修饰的变量、方法属于类变量、类方法, // 可以通过【类名.变量名】、【类名.方法名】直接引用,而不需要派生一个类实例出来。 public static Mgr01 getInstance(){return INSTANCE;} public void m(){System.out.println("m");}//主方法 // 测试一把,是否单例 public static void main(String args[]){ Mgr01 m1 = Mgr01.getInstance(); Mgr01 m2 = Mgr01.getInstance(); System.out.println(m1 == m2); } }
用static语句块来做初始化
package com.DesignPattern.singleton; /* 饿汉式 */ public class Mgr02 { private static final Mgr02 INSTANCE; static { INSTANCE = new Mgr02(); } private Mgr02() { }//私有构造函数,禁止外部访问 public static Mgr02 getInstance() { return INSTANCE; } public void m() { System.out.println("m"); }//主方法 // 测试一把,是否单例 public static void main(String args[]) { Mgr02 m1 = Mgr02.getInstance(); Mgr02 m2 = Mgr02.getInstance(); System.out.println(m1 == m2); } }
在使用时再进行类的实例化。
使用if语句避免多次实例化,缺点是多线程访问时无法保证唯一性。
package com.DesignPattern.singleton; /* 懒汉式 非线程安全 */ public class Mgr03 { private static Mgr03 INSTANCE; private Mgr03() { }//私有构造函数,禁止外部访问 public static Mgr03 getInstance() { if (INSTANCE == null) { try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Mgr03(); } return INSTANCE; } public void m() { System.out.println("m"); }//主方法 // 测试一把,是否单例 public static void main(String args[]) { for(int i=0;i<100;i++){ new Thread(new Runnable() { @Override public void run() { System.out.println(Mgr03.getInstance().hashCode()); } }).start(); } } }
加锁保证线程安全,缺点是给整个获取实例的语句块加锁导致效率降低。
package com.DesignPattern.singleton; /* 懒汉式 加锁保证线程安全 但整个语句块加锁导致效率降低 */ public class Mgr04 { private static Mgr04 INSTANCE; private Mgr04() { }//私有构造函数,禁止外部访问 public static synchronized Mgr04 getInstance() { if (INSTANCE == null) { try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Mgr04(); } return INSTANCE; } public void m() { System.out.println("m"); }//主方法 // 测试一把,是否单例 public static void main(String args[]) { for(int i=0;i<100;i++){ new Thread(new Runnable() { @Override public void run() { System.out.println(Mgr04.getInstance().hashCode()); } }).start(); } } }
试图减小同步代码块以提高效率,下面这种写法不可行。不可行的原因是,if语句执行过的线程,都会进入执行同步语句块。if语句判断是否存在实例没有加锁,给执行语句块加锁没有意义。
所以这种写法是线程不安全的,加锁没有意义。
package com.DesignPattern.singleton; /* 懒汉式 试图减小同步代码块以提高效率,这种写法不可行 */ public class Mgr05 { private static Mgr05 INSTANCE; private Mgr05() { }//私有构造函数,禁止外部访问 public static Mgr05 getInstance() { if (INSTANCE == null) { //试图通过减小同步代码块的方式提高效率,这种写法不可行 synchronized (Mgr05.class){ try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Mgr05(); } } return INSTANCE; } public void m() { System.out.println("m"); }//主方法 // 测试一把,是否单例 public static void main(String args[]) { for(int i=0;i<100;i++){ new Thread(new Runnable() { @Override public void run() { System.out.println(Mgr05.getInstance().hashCode()); } }).start(); } } }
双重检查单例写法,懒汉式。是上一种写法的改良,相比写法2提升了效率,因为不是每个线程都需要加锁了,提高了并发。
在类被一个线程实例化之后,尚未进入第一层if的线程不会执行同步块语句;已经进入同步块之内的线程,执行完第二层if就会归还锁。
注意:为了多线程创建和访问的安全性,需要加volatile关键字。
package com.DesignPattern.singleton; /* 懒汉式 同步代码块+双判断 线程安全且高效 */ public class Mgr06 { private static volatile Mgr06 INSTANCE; private Mgr06() { }//私有构造函数,禁止外部访问 public static Mgr06 getInstance() { //第一层用于提高效率,一旦类已被实例化,就不再往下执行 if (INSTANCE == null) { synchronized (Mgr06.class){ //第二层用于保证单例,防止进入第一层if的线程,获取锁之后再次实例化类 if(INSTANCE==null){ try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Mgr06(); } } } return INSTANCE; } public void m() { System.out.println("m"); }//主方法 // 测试一把,是否单例 public static void main(String args[]) { for(int i=0;i<100;i++){ new Thread(new Runnable() { @Override public void run() { System.out.println(Mgr06.getInstance().hashCode()); } }).start(); } } }
在类的内部定义静态内部类,在该静态内部类中定义初始化的外部类的静态实例。
在获取外部类的实例的时候,返回静态内部类定义的经过初始化的外部类的静态实例。
由于加载外部类时不会加载内部类,这样可以实现懒加载。
JVM保证单例,因为JVM在加载类的时候只会加载一次。
package com.DesignPattern.singleton; /* 静态内部类方式 JVM保证单例 由于加载外部类时不会加载内部类,这样可以实现懒加载 */ public class Mgr07 { private Mgr07(){};//私有构造函数,禁止外部访问 private static class Mgr07Holder{ private static final Mgr07 INSTANCE = new Mgr07(); } public static Mgr07 getInstance(){ return Mgr07Holder.INSTANCE; } public void m(){System.out.println("m");}//主方法 // 测试一把,是否单例 public static void main(String args[]){ for(int i=0;i<100;i++){ new Thread(new Runnable() { @Override public void run() { System.out.println(Mgr06.getInstance().hashCode()); } }).start(); } } }
既能保证线程安全,也可以防止反序列化。(没有深究,这种写法没太理解)
package com.DesignPattern.singleton;
public enum Mgr08 {
INSTANCE;
public void m(){}
// 测试一把,是否单例
public static void main(String args[]){
for(int i=0;i<100;i++){
new Thread(() -> System.out.println(Mgr08.INSTANCE.hashCode())).start();
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。