java 单例模式
java 单例模式是什么?
单例模式:确保一个类只有一个实例,并提供一个全局访问点。如果使用多个类加载器,可能导致单例失效而产生多个实例。
实例如下:
// 经典单例模式:此例不是线程安全的例子
// 优点:在获取实例的方法中,进行实例的初始化,节省系统资源
// 缺点:1.如果获取实例时,初始化工作较多,加载速度会变慢,影响系统系能
// 2.每次获取实例都要进行非空检查,系统开销大
// 3.非线程安全,当多个线程同时访问getInstance()时,可能会产生多个实例
class Singleton {
private static Singleton uniqueInstance; // 1.静态的单例变量
private Singleton() {} // 2.私有的构造器
public static Singleton getInstance() { // 3.静态方法
if ( uniqueInstance == null ) { // 延迟实例化
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// 这里还有其他有用的方法
public String getDescription() { // 获取描述
return "I'm a classic Singleton!";
}
}
class SingletonClient { // 单例客户端
public static void main( String[] args ) {
Singleton singleton = Singleton.getInstance();
System.out.println( singleton.getDescription() );
}
}
public class Test0524{
public static void main( String[] args ){
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println( "s1 == s2 : " + ( s1 == s2 ) );
}
}
----------------------------------------------------------------
E:\java>java Test0524
s1 == s2 : true
// 线程不安全的单例怎么样解决?
class ChocolateBoiler { // 巧克力锅炉
private boolean empty; // 空的
private boolean boiled; // 煮好的
private static ChocolateBoiler uniqueInstance; // 单一实例
private ChocolateBoiler() {
empty = true;
boiled = false;
}
public static ChocolateBoiler getInstance() { // 获取实例
if ( uniqueInstance == null ) {
System.out.println( "Creating unique instance of Chocolate Boiler" );
uniqueInstance = new ChocolateBoiler();
}
System.out.println( "Returning instance of Chocolate Boiler" );
return uniqueInstance;
}
public void fill() { // 注满
if ( isEmpty() ) {
empty = false;
boiled = false;
// fill the boiler with a milk/chocolate mixture
}
}
public void drain() { // 沥干
if ( !isEmpty() && isBoiled() ) {
// drain the boiled milk and chocolate
empty = true;
}
}
public void boil() { // 煮
if ( !isEmpty() && !isBoiled() ) {
// bring the contents to a boil
boiled = true;
}
}
public boolean isEmpty() { // 空的
return empty;
}
public boolean isBoiled() { // 煮好的
return boiled;
}
}
class ChocolateController { // 巧克力控制器
public static void main( String args[] ) {
ChocolateBoiler boiler = ChocolateBoiler.getInstance();
boiler.fill();
boiler.boil();
boiler.drain();
// will return the existing instance
ChocolateBoiler boiler2 = ChocolateBoiler.getInstance();
}
}
// 1.同步getInstance()方法/ 线程安全但性能差。
// 优点:线程安全
// 缺点:每次获取实例都要加锁,耗费资源,其实只要实例已经生成,以后获取就不需要再锁了
class Singleton {
private static Singleton uniqueInstance;
// 这里可以加入其他有用的实例变量
private Singleton() {}
// 同步一个方法可能造成程序执行效率下降100倍。
public static synchronized Singleton getInstance() { // 同步的
if ( uniqueInstance == null ) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a thread safe Singleton!";
}
}
class SingletonClient {
public static void main( String[] args ) {
Singleton singleton = Singleton.getInstance();
System.out.println( singleton.getDescription() );
}
}
// 2.急切实例化/ 饿汉式:不用延迟实例化
// 好处:线程安全;获取实例速度快
// 缺点:类加载即初始化实例,内存浪费
class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a statically initialized Singleton!";
}
}
class SingletonClient {
public static void main( String[] args ) {
Singleton singleton = Singleton.getInstance();
System.out.println( singleton.getDescription() );
}
}
// 3.双重检查加锁/ 此例在Java 5之前不能保证单例的实现。
// 优点:线程安全,进行双重检查,保证只在实例未初始化前进行同步,效率高
// 缺点:还是实例非空判断,耗费一定资源
class Singleton { // 单例模式
private volatile static Singleton uniqueInstance; // volatile:轻量级同步机制
private Singleton() {}
public static Singleton getInstance() { // 获取实例
if ( uniqueInstance == null ) {
synchronized ( Singleton.class ) { // 重量级同步锁
if ( uniqueInstance == null ) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
class SingletonClient { // 单例客户端
public static void main( String[] args ) {
Singleton singleton = Singleton.getInstance();
}
}
// 4.静态内部类
// 优点:既避免了同步带来的性能损耗,又能够延迟加载
class Singleton {
private Singleton(){}
private static class Holder {
private static Singleton singleton = new Singleton();
}
public static Singleton getSingleton(){
return Holder.singleton;
}
}
// 上面提到的所有实现方式都有两个共同的缺点:
// 1.需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对
// 象实例时都会创建一个新的实例。
// 2.可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实
// 例的时候抛异常)。
// 5.枚举类型:天然线程安全,可防止反射生成实例。
// 使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的
// 对象。因此,Effective Java推荐尽可能地使用枚举来实现单例。
enum Singleton { // 枚举类型
INSTANCE; // 单例常量
private String name;
public String getName(){
return name;
}
public void setName( String name ){
this.name = name;
}
}
public class Test0524{ // 枚举类型的用法
public static void main( String[] args ){
Singleton singleton = Singleton.INSTANCE;
singleton.setName( "This is a SingletonInstance" );
System.out.println( singleton.getName() );
}
}
// 继承会导致所有的派生类共享同一个实例变量
class Singleton {
protected static Singleton uniqueInstance;
// other useful instance variables here
protected Singleton() {} // 别的类也可以实例化它
public static synchronized Singleton getInstance() {
if ( uniqueInstance == null ) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
}
class CoolerSingleton extends Singleton { // 冷却器单例
// useful instance variables here
protected static Singleton uniqueInstance;
private CoolerSingleton() {
super();
}
// useful methods here
}
class HotterSingleton extends Singleton { // 加热器单例
// useful instance variables here
private HotterSingleton() {
super();
}
// useful methods here
}
class SingletonTestDrive {
public static void main( String[] args ) {
Singleton foo = CoolerSingleton.getInstance();
Singleton bar = HotterSingleton.getInstance();
System.out.println( foo );
System.out.println( bar );
}
}