随手记。
单例模式作为23种设计模式种比较经典的,一般都要求能够手写(很简单),下面写一下两种实现方式:
1.饿汉式
步骤:
1.新建一个类,提供私有构造器
2.使用构造器声明当前对象实例成员
3.声明public static的返回当前类对象的方法
4.要在方法中使用私有对象成员,要将实例成员声明为static的
public class Test {
// 饿汉式 单例模式
public static void main(String[] args) {
Bank b1 = Bank.getInstance();
Bank b2 = Bank.getInstance();
System.out.println(b1 == b2);
}
}
class Bank {
// 1.私有化构造器
private Bank() {
}
// 2.声明当前类对象
// 4.此对象也必须声明成static的
private static Bank instance = new Bank();
// 3.声明public、static的返回当前类对象的方法
public static Bank getInstance() {
return instance;
}
}
懒汉式
步骤与饿汉式相同,存在两个小细节上的不同。
public class Test {
// 懒汉式 单例模式
public static void main(String[] args) {
Bank b1 = Bank.getInstance();
Bank b2 = Bank.getInstance();
System.out.println(b1 == b2);
}
}
class Bank {
// 1.私有化构造器
private Bank() {
}
// 2.声明当前类对象,将其赋为null
// 4.此对象也必须声明成static的
private static Bank instance = null;
// 3.声明public、static的返回当前类对象的方法
public static Bank getInstance() {
if (instance == null) {
instance = new Bank();
}
return instance;
}
}
如何区分饿汉式和懒汉式?
饿汉式:在声明实例成员的时候就造好对象。
懒汉式:在调用方法时才造对象。
对比一下两者的不同:
饿汉式:对象加载时间长;线程安全。
懒汉式:延迟了对象的加载;目前这种写法线程不安全,例如三个线程几乎同时第一次调用这个方法返回Bank类的实例,这时instance是null,三个线程都进入了if,最后在内存中生成了三个Bank实例,违反了单例原则。
3.线程安全的懒汉式单例模式
这里不关心调用,只关心如何在类中实现。
public class SingletonLazy {
public static void main(String[] args) {
}
}
class Bank {
private Bank() {
}
private static Bank instance = null;
//public static synchronized Bank getInstance() {//方式一第一种写法,同步监视器为this
public static Bank getInstance() {
//方式一第二种写法:效率稍差
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
return instance;
}
}
}
上面的方式有弊端:在调用方法,内存中已有实例之后,后续多个线程 a b c 调用方法时,都会进入同步代码块,这就涉及到线程阻塞等问题,例如 a 抢到了执行权,则 b c 需要等待,浪费时间,效率不高。
第二种方式:除了与第一个调用方法的线程几乎一起到达的其他线程会进入同步代码块中判断,后面再来调用方法的多个线程 a b c,直接在外层 if 就走了,不会进入同步代码块,不会涉及到线程阻塞(排队)。
public class SingletonLazy {
public static void main(String[] args) {
}
}
class Bank {
//私有化构造器
private Bank() {
}
private static Bank instance = null;
public static Bank getInstance() {
//方式二:
if (instance == null) {
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
无锁的线程安全单例模式:Java中单例模式的最佳实现中,类只会被加载一次,通过声明时直接实例化静态成员的方式来保证一个类只有一个实例。这种实现方式避免了使用同步锁机制和判断实例是否被创建的额外检查:
class Bank {
//私有化构造器
private Bank() {
}
private static final Bank instance = new Bank();
public static synchronized Bank getInstance() {
return instance;
}
}
在最新版本Java中,类只有在使用的时候才会被加载,所以也算是一种延迟加载模式(懒汉)。类的加载时机取决于JVM的实现机制,版本之间会有不同。