singleton模式四种线程安全的实现

1.描述:

Singleton(单例)是设计模式的一种,为了保证一个类仅有一个实例,并提供一个访问它的全局访问点。

2.主要特点:

1)单例类确保自己只有一个实例(构造函数私有:不被外部实例化,也不被继承)。

2)单例类必须自己创建自己的实例。

3)单例类必须为其他对象提供唯一的实例。

3.单例模式的应用:

资源管理器,回收站,打印机资源,线程池,缓存,配置信息类,管理类,控制类,门面类,代理类通常被设计为单例类

如果程序有多个类加载器又同时使用单例模式就有可能多个单例并存就要找相应解决方法了

4.实现方法:

如果应用程序总是创建并使用单例实例或在创建和运行时开销不大。

1).Eager initialization 饿汉式单例类(依赖jvm在加载类时创建唯一单例实例)

public class EagerSingleton {
        // jvm保证在任何线程访问uniqueInstance静态变量之前一定先创建了此实例
        private static EagerSingleton uniqueInstance = new EagerSingleton();  

        // 私有的默认构造子,保证外界无法直接实例化
        private EagerSingleton() {
        }  

        // 提供全局访问点获取唯一的实例
        public static EagerSingleton getInstance() {
                return uniqueInstance;
        }
}

如果开销比较大,希望用到时才创建就要考虑延迟实例化,或者Singleton的初始化需要某些外部资源(比如网络或存储设备),就要用后面的方法了.

2)Lazy
initialization 懒汉式单例类

public class LazySingleton {
        private static LazySingleton uniqueInstance;  

        private LazySingleton() {
        }  

        public static synchronized LazySingleton getInstance() {
                if (uniqueInstance == null)
                        uniqueInstance = new LazySingleton();
                return uniqueInstance;
        }
} 

同步一个方法可能造成程序执行效率下降100倍,完全没有必要每次调用getInstance都加锁,事实上我们只想保证一次初始化成功,其余的快速返回而已,如果在getInstance频繁使用的地方就要考虑重新优化了.

3)"双检锁"(Double-Checked Lock)尽量将"加锁"推迟,只在需要时"加锁"(仅适用于java 5.0 以上版本,volatile保证原子操作)

happens-before:"什么什么一定在什么什么之前运行",也就是保证顺序性.

现在的CPU有乱序执行的能力(也就是指令会乱序或并行运行,可以不按我们写代码的顺序执行内存的存取过程),并且多个CPU之间的缓存也不保证实时同步,只有上面的happens-before所规定的情况下才保证顺序性.

JVM能够根据CPU的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能.

如果没有volatile修饰符则可能出现一个线程t1的B操作和另一线程t2的C操作之间对instance的读写没有happens-before,可能会造成的现象是t1的B操作还没有完全构造成功,但t2的C已经看到instance为非空,这样t2就直接返回了未完全构造的instance的引用,t2想对instance进行操作就会出问题.

volatile 的功能:

1. 避免编译器将变量缓存在寄存器里

2. 避免编译器调整代码执行的顺序

优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

public class DoubleCheckedLockingSingleton {
        // java中使用双重检查锁定机制,由于Java编译器和JIT的优化的原因系统无法保证我们期望的执行次序。
        // 在java5.0修改了内存模型,使用volatile声明的变量可以强制屏蔽编译器和JIT的优化工作
        private volatile static DoubleCheckedLockingSingleton uniqueInstance;  

        private DoubleCheckedLockingSingleton() {
        }  

        public static DoubleCheckedLockingSingleton getInstance() {
                if (uniqueInstance == null) {
                        synchronized (DoubleCheckedLockingSingleton.class) {
                                if (uniqueInstance == null) {
                                        uniqueInstance = new DoubleCheckedLockingSingleton();
                                }
                        }
                }
                return uniqueInstance;
        }
}  

4)Lazy initialization holder class 满足所有 Double-Checked Locking 满足的条件,并且没有显示的同步操作

public class LazyInitHolderSingleton {
        private LazyInitHolderSingleton() {
        }  

        private static class SingletonHolder {
                private static final LazyInitHolderSingleton INSTANCE = new LazyInitHolderSingleton();
        }  

        public static LazyInitHolderSingleton getInstance() {
                return SingletonHolder.INSTANCE;
        }
}  

根据jvm规范,当某对象第一次调用LazyInitHolderSingleton.getInstance()时,LazyInitHolderSingleton类被首次主动使用,jvm对其进行初始化(此时并不会调用LazyInitHolderSingleton()构造方法),然后LazyInitHolderSingleton调用getInstance()方法,该方法中,又首次主动使用了SingletonHolder类,所以要对SingletonHolder类进行初始化,初始化中,INSTANCE常量被赋值时才调用了 LazyInitHolderSingleton的构造方法LazyInitHolderSingleton(),完成了实例化并返回该实例。

当再有对象(也许是在别的线程中)再次调用LazyInitHolderSingleton.getInstance()时,因为已经初始化过了,不会再进行初始化步骤,所以直接返回INSTANCE常量即同一个LazyInitHolderSingleton实例。

时间: 2024-07-30 23:52:46

singleton模式四种线程安全的实现的相关文章

OSG视景器四种线程模型(摘自Array《最长的一帧》)

OSG 的视景器包括四种线程模型,可以使用setThreadingModel 进行设置,不同的线程 模型在仿真循环运行时将表现出不同的渲染效率和线程控制特性.通常而言,这四种线程的 特性如下: SingleThreaded:单线程模型.OSG 不会创建任何新线程来完成场景的筛选和渲染,因 而也不会对渲染效率的提高有任何助益.它适合任何配置下使用. CullDrawThreadPerContext:OSG 将为每一个图形设备上下文(GraphicsContext)创建 一个图形线程,以实现并行的渲

jdk提供的四种线程池

一.线程池什么时候用,有什么好处? "线程池"顾名思义,就是存放线程的池子,这个池子可以存放多少线程取决于采用哪种线程池,取决于有多少并发线程,有多少计算机的硬件资源.使用线程池最直接的好处就是:线程可以重复利用.减少创建和销毁线程所带来的系统资源的开销,提升性能(节省线程创建的时间开销,使程序响应更快). 二.JDK自带4种的线程池(JDK1.5之后) 2.1.固定线程数的线程池(newFixedThreadPool) 这种线程池里面的线程被设计成存放固定数量的线程,具体线程数可以考

Java程序猿必会的四种线程池

前言,对于Java程序猿来说,线程池是面试高频题,是我们必须掌握的一个技能,本篇文章主要给大家讲解四种线程池的使用. 线程池简介 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务. 线程池的工作机制: 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给

Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? Java new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start(); 1 2 3 4 5 6 7 new Thread(new

Java四种线程池

Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:2015-10-20 22:37:40      阅读:8762      评论:0      收藏:0      [点我收藏+] 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异

Java四种线程池的使用

Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待.newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行.newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有

JAVA四种线程池实例

1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? Java 1 2 3 4 5 6 7 new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start(); 那你就out太多了,new Thread的弊端如下: a. 每次new Thread新建对象性能差. b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及

Java四种线程池的学习与总结

在Java开发中,有时遇到多线程的开发时,直接使用Thread操作,对程序的性能和维护上都是一个问题,使用Java提供的线程池来操作可以很好的解决问题. 一.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable(){ @Override public void run(){ //TODO Auto-generatedmethod stub } } ).start(); 那你就out太多了,new Thread的弊端如下:

Executors提供四种线程池

JAVA并发编程--EXECUTORS 线程池的思想是一种对象池的思想,开放一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完毕,对象归还给池.这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源. 代码: http://www.cnblogs.com/chenjingjing/articles/1683745.html 一.固定大小的线程池 newFixedThreadPool  二.单任务线程池 newSingleT