单例模式的七种实现-Singleton(Java实现)
2018-06-18 01:28:37来源:未知 阅读 ()
饿汉式
实现代码:
public class Singleton {
private Singleton() {
}
private static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
验证一下:
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
// true
}
如果用反射, 是否仍然是单例:
结果是反射破坏了单例
public static void main(String[] args) throws Exception {
// 自定义单例方法获取
Singleton s1 = Singleton.getInstance();
// 反射获取
Constructor constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton s2 = (Singleton) constructor.newInstance();
System.out.println(s1 == s2);
//false
}
懒汉式
将上面的饿汉式改为懒汉式:
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
验证一下:
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
// true
}
不过这是一种线程不安全的单例实现.
我们在Singleton中加上sleep来模拟一下线程切换:
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton = new Singleton();
}
return singleton;
}
}
验证一下线程不安全:
public class Main3 {
private static LinkedBlockingQueue<Singleton> singletons = new LinkedBlockingQueue<>();
public static void main(String[] args) throws Exception{
ExecutorService threadPool = Executors.newFixedThreadPool(10);
for(int i= 0;i<100;i++){
threadPool.execute(()->{
singletons.offer(Singleton.getInstance());
});
}
Singleton basic = singletons.take();
while(basic==singletons.take()){
System.out.println("continue");
continue;
}
System.out.println("走到这里说明单例失败");
}
}

懒汉式+同步方法
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public synchronized static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
验证一下:
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
// true
}
由于是同步, 所以同步方法内不会出现多线程执行的情况.
懒汉式+双重校验锁
因为上面那种会每次进时都会进行同步锁, 很浪费性能, 所以在加锁之间先进行校验
public class Singleton{
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton==null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
验证一下性能:
能明显看出来性能差距...5千倍...
同步方法, 即直接在方法声明处加了Synchronize的情况:
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

双重校验锁:
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

懒汉式+双重校验锁+防止指令重拍
看似简单的一段赋值语句:instance = new Singleton(); 其实JVM内部已经转换为多条指令:
memory = allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址
但是经过重排序后如下:
memory = allocate(); //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没被初始化
ctorInstance(memory); //2:初始化对象
可以看到指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面,在线程A初始化完成这段内存之前,线程B虽然进不去同步代码块,但是在同步代码块之前的判断就会发现instance不为空,此时线程B获得instance对象进行使用就可能发生错误。
加上volatile关键字:
public class Singleton {
private Singleton() {
}
private volatile static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
验证一下性能:
能明显看出来加了volatile后对性能的影响, 由之前的5, 变为了302...
性能下降了, 但是相比于上面的双重校验锁, 更保证了线程安全.
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

静态内部类
这也是一种很好的实现方式, 不仅懒加载, 还保证了线程安全, 性能也很好, 实现起来也很简单
public class Singleton {
private static class LazyHolder {
private static final Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return LazyHolder.instance;
}
}
验证一下性能:
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

枚举
个人对枚举类型的理解还有限, 有待学习....
public enum Singleton {
INSTANCE;
private String name;
Singleton() {
this.name = "king";
}
public static Singleton getInstance() {
return INSTANCE;
}
public String getName() {
return this.name;
}
}
验证一下性能:
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:缓存与数据库的一致性
下一篇:Java学习过程中的收获
- 设计模式-委派/策略模式 2020-06-09
- 深入理解:设计模式中的七大设计原则 2020-06-07
- 设计模式---类之间的关系知多少 2020-06-07
- 你与面试官所了解的单例模式并不一样! 2020-06-06
- 高手眼中的观察者模式有什么不一样 2020-06-05
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash
