单例模式几种实现的优缺点
1、懒汉式(静态变量)
public class SingletonTest1 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance==instance1);
}
}
//饿汉式(静态常量)单例模式
class Singleton{
//构造器私有化,防止外部new
private Singleton(){}
//类内部创建实例对象
private final static Singleton instance=new Singleton();
//提供一个共有的静态方法返回对象实例
public static Singleton getInstance(){
return instance;
}
}
分析
优点1 简单,类加载的时候创建实例,(基于classloader机制)不会产生线程同步的问题
缺点2 类装在的时候初始化实例,没有达到lazy loading的效果,因为如果因为某些原因导致类装载了 而不是getInstance触发的,一直没有用到实例对象,就会造成资源浪费
2、饿汉式(静态代码)
public class SingletonTest2 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance==instance1);
}
}
class Singleton{
private Singleton(){}
private static Singleton instance;
//通过静态代码块实现
static {
instance=new Singleton();
}
public static Singleton getInstance(){
return instance;
}
}
分析
也是类装载的时候就执行静态代码块,可能造成资源浪费
3、懒汉式(线程不安全)
public class SingletonTest3 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance==instance1);
}
}
//懒汉式,使用时才进行初始化
class Singleton{
private Singleton(){}
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
instance=new Singleton();
}
return instance;
}
}
分析
用到时候才创建,达到了lazy loading的效果,但是只能单线程下使用
因为如果多个线程同时走到了 if(instance==null)这个语句,多个线程都会new Singleton(); 这导致的是错误!!! 不能用
4、懒汉式线程同步(效率低)
public class SingletonTest4 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance==instance1);
}
}
//懒汉式,使用时才进行初始化
class Singleton{
private Singleton(){}
private static Singleton instance;
public static synchronized Singleton getInstance(){
if(instance == null){
instance=new Singleton();
}
return instance;
}
}
分析
syntronized 同步线程,不会多个线程同时执行这个代码
新的问题:
效率变低,多个线程想获得实例的时候,都要进行线程同步,但是这个方法只执行一次就够了,其他的方法直接return, 也不推荐!
5、懒汉式 (双重检查)
public class SingletonTest5 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance==instance1);
}
}
//双重检查
class Singleton{
private Singleton(){}
private static volatile Singleton instance;
public static synchronized Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance=new Singleton();
}
}
}
return instance;
}
分析
同步代码块,只同步创建实例的部分,其他不同步,不会造成浪费,多个线程,只有同时达到第一个 if(instance==null)的时候才会进行线程同步 创建对象
6、静态内部类实现
public class SingletonTest6 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance==instance1);
System.out.println(instance);
}
}
//静态内部类实现
class Singleton{
private static Singleton instance;
//防止外部创建实例
private Singleton(){}
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
public static synchronized Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
分析
- Singleton加载的时候 不会导致内部类SingletonInstance加载,保证了懒加载,调用getInstance的时候才会加载内部类 内部类 延迟加载!
- 装载类的时候是线程安全的,所以这种方法是线程安全的(使用了jvm的类装载机制,类初始化的时候别的线程是无法进入的)
7、枚举实现(这里实现有问题 我不会)
public class SingletonTest7 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance==instance1);
System.out.println(instance);
}
}
//使用枚举实现
enum Singleton{
INSTANCE;
public static Singleton getInstance(){
return INSTANCE;
}
}
分析
不仅避免线程同步问题,还能防止反序列化重新创建对象
样例:jdk内部的Runtime类 就是使用饿汉式的单例模式,因为这个类每次都用,所以不会资源浪费
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() {}
...
}