单例的实现
基于预初始化且线程安全的单例模式
package online.javabook.gof.creational.patterns6.singleton;
public class NotLazyAndThreadSafeSingleton {
private static NotLazyAndThreadSafeSingleton instance = new NotLazyAndThreadSafeSingleton();
private NotLazyAndThreadSafeSingleton(){
}
public static NotLazyAndThreadSafeSingleton getInstance() {
return instance;
}
}
基于懒加载且线程不安全的单例模式(单线程下可用)
package online.javabook.gof.creational.patterns6.singleton;
public class LazyButNotThreadSafeSingleton {
private static LazyButNotThreadSafeSingleton instance;
private LazyButNotThreadSafeSingleton(){
}
public static LazyButNotThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new LazyButNotThreadSafeSingleton();
}
return instance;
}
}
基于同步懒加载且线程安全的单例模式(但是性能被synchronized影响)
package online.javabook.gof.creational.patterns6.singleton;
public class LazyAndThreadSafeSingleton {
private static LazyAndThreadSafeSingleton instance;
private LazyAndThreadSafeSingleton(){
}
public static synchronized LazyAndThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new LazyAndThreadSafeSingleton();
}
return instance;
}
}
基于基于双重检查的单例模式
package online.javabook.gof.creational.patterns6.singleton;
public class DoubleCheckedLockingSingleton {
private volatile static DoubleCheckedLockingSingleton singleton;
private DoubleCheckedLockingSingleton(){
}
public static DoubleCheckedLockingSingleton getSingleton() {
if (singleton == null) {
synchronized (DoubleCheckedLockingSingleton.class) {
if (singleton == null) {
singleton = new DoubleCheckedLockingSingleton();
}
}
}
return singleton;
}
}
基于静态内部类的单例模式
package online.javabook.gof.creational.patterns6.singleton;
public class StaticInnerClassSingleton {
private static class SingletonHolder {
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
private StaticInnerClassSingleton(){
}
public static final StaticInnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
静态代码块的同步访问例子
JVM保证类的
package online.javabook.jvm.classinit.syncstaticblock;
public class ClassWithStaticBlock {
static {
System.out.println("被线程:" + Thread.currentThread() + "加载中");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("被线程:" + Thread.currentThread() + "加载结束");
}
}
package online.javabook.jvm.classinit.syncstaticblock;
public class SyncLoadStaticBlockTest {
public static void main(String[] args) {
Thread thread1 = new Thread("线程1") {
@Override
public void run() {
new ClassWithStaticBlock();
}
};
Thread thread2 = new Thread("线程1") {
@Override
public void run() {
new ClassWithStaticBlock();
}
};
thread1.start();
thread2.start();
}
}
# 说明:静态块中类加载时只能被一个线程同步初始化
被线程:Thread[线程1,5,main]加载中
被线程:Thread[线程1,5,main]加载结束
对单例的序列化和反序列化
如果一个原本期望设计成单例的对象,即便没有public的构造函数,使用了预创建对象,或者线程安全的延迟创建对象。但是如果对象本身是一个可以序列化的对象,则会绕过这些制约,导致对象经过序列化和反序列化后创建出新的实例。
package online.javabook.design.gof.creational6.singleton;
import com.sun.corba.se.impl.orbutil.ObjectUtility;
import online.javabook.design.gof.creational5.prototype.graph.palette.product.LineGraphPrototype;
import java.io.*;
public class LazyAndReadResolveAndThreadSafeSingleton implements Serializable {
private static LazyAndReadResolveAndThreadSafeSingleton instance = new LazyAndReadResolveAndThreadSafeSingleton();
private LazyAndReadResolveAndThreadSafeSingleton(){
}
public static synchronized LazyAndReadResolveAndThreadSafeSingleton getInstance() {
return instance;
}
/*private Object readResolve() {
return instance;
}*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
LazyAndReadResolveAndThreadSafeSingleton instance1 = LazyAndReadResolveAndThreadSafeSingleton.getInstance();
System.out.println("instance1:"+instance1);
// out
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(instance1);
// in
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
LazyAndReadResolveAndThreadSafeSingleton instance2 = (LazyAndReadResolveAndThreadSafeSingleton) ois.readObject();
oos.close();
ois.close();
System.out.println("instance2:"+instance2);
}
}
经过序列化和反序列化出来的实例已经是一个新的对象了。它绕过了原本单例的初衷。
instance1:online.javabook.design.gof.creational6.singleton.LazyAndReadResolveAndThreadSafeSingleton@29453f44
instance2:online.javabook.design.gof.creational6.singleton.LazyAndReadResolveAndThreadSafeSingleton@4769b07b
为了避免这个问题,可以在添加readResolve方法强制返回instance实例,避免反序列化后返回新的对象。重新执行代码,会发现即便通过序列化和反序列化也仍然会返回同样的对象实例。
虽然如此,这样做容易把事情弄的太过于复杂,在java开发中经常会采用约定的编程方式,例如约定这个类会是单例,哪怕构造函数是public的,如果已经有框架来托管对象的创建,就不要考虑自己再实例化出一个新对象了。
instance1:online.javabook.design.gof.creational6.singleton.LazyAndReadResolveAndThreadSafeSingleton@29453f44
instance2:online.javabook.design.gof.creational6.singleton.LazyAndReadResolveAndThreadSafeSingleton@29453f44
现实世界中的单例
Runtime.getRuntime();
预初始化的单例在现实世界中是最常用的,没有那么多花里胡哨。那为什么会有这么多花里胡哨的实现呢,之前说过设计模式很多都是来自客户端程序鼎盛的时期,我猜测是因为一些基于插件机制的架构,一个本地应用存在很多插件实例,但是中实际的使用过程中,用户未必会每个子插件都会点开使用,这个时候延迟加载就派上用场了。但是中web中即便是插件机制,作为一个非单机,非单用户的系统,很少会有哪个模块不会被触发。
package java.lang;
import java.io.*;
import java.util.StringTokenizer;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
/**
* Every Java application has a single instance of class
* <code>Runtime</code> that allows the application to interface with
* the environment in which the application is running. The current
* runtime can be obtained from the <code>getRuntime</code> method.
* <p>
* An application cannot create its own instance of this class.
*
* @author unascribed
* @see java.lang.Runtime#getRuntime()
* @since JDK1.0
*/
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}