单例模式写法------java
单例模式指强制要求某个类,在某个程序中只有唯一实例(对象),不允许创建多个实例。单例模式思想的基本分类:1.饿汉思想【类加载的时候就创建出来,类中创建的对象外面可获取,尽早创建,属于提前初始化】;2.懒汉思想【尽量晚的创建实例,第一次调用函数时,才new 对象,属于延迟初始化】;这两个基本分类延伸出多种单例模式的写法,不过饿汉思想只有2种写法,其余全是懒汉思想延伸出去的,这是由于饿汉方式是天生线程安全的,但基本的懒汉方式是线程不安全的,为了优化它,人们想出不同的写法,去保证懒汉思想下线程安全。接下我将分别介绍这些写法。
一、饿汉思想的写法
1.静态常量饿汉写法:
//1.静态常量饿汉写法
public class Single{
static final Single sg=new Single();//final修饰静态引用变量是指这个变量的引用地址,不能指向 其它对象
private Single(){}
public static Single getInstance(){
return sg;
}
}
2.静态代码块饿汉写法:
2.静态代码块的饿汉写法
public class Single{
private static Single instance;
static{
instance=new Single();
}
private Single(){
}
public static Single getInstanse(){
return instance;
}
}
以上是饿汉思想的所有写法。
二、懒汉思想的写法
1.基本的懒汉思想的写法【线程不安全】:
//懒汉思想的写法
//1.线程不安全基本写法
public class lazyman {
private static lazyman instance;
private lazyman(){
}
public static lazyman getInstance(){
if(instance==null){
instance=new lazyman();
}
return instance;
}
}
【面试(饿汉、懒汉思想是否线程安全,若不是该咋办?):在多线程环境下调用,饿汉式对象在类加载时就创建好了,之后所有线程只是读取,不会再创建,jvm类加载机制会保证只创建一次对象,懒汉式思想是线程不安全的,一个是因为对象是第一次调用getInstance()才会被创建,多线程同时调用,会同时进入if(instance==null),导致创建多个对象,单例失败,另一个因为是多个线程对同一静态变量instance做赋值操作,写竞争、线程不安全,最终还可能被后面的对象覆盖,浪费创建的开销。如何解决?1.加锁,将条件与修改加锁打包成原子操作,后执行的线程会在此处阻塞,当它进入时,前面的线程早就是对象不为null,自然就无法重新创建对象,但若未出现线程安全问题时,每调用一次方法,就加锁相互阻塞,影响程序执行效率,于是我们采用2.按需加锁,真正涉及到线程安全问题再加锁,不涉及就不加,在加锁前加入一个条件判断,判断实例是否被创建,未创建才加锁。还有两个问题就是内存可见性问题(线程改了变量instance没写入主内存但其它线程看不见误以为还是空)、指令重排序问题(new的过程中指令重排序,导致对象半初始化 ,即先分配内存,把地址赋给instance但还没初始化对象 )解决方法是3.给对象加volatile,确保每次读取操作,都是读主内存与此变量的读取与修改不会触发指令重排序。对于懒汉思想还有一种天生线程安全的写法是4.使用静态内部类,在静态内部类中创建静态常量对象】
2.加锁将条件与修改打包为原子操作写法——优化2.0版(仍有问题)
2.将条件判断与修改打包成原子操作1.0版
public class lazyman{
private static lazyman instance;
private lazyman(){
}
public static synchronized lazyman getInstance(){
if(instance==null){
instance=new lazyman();
}
return instance;
}
}
3.按需加锁(进入加锁前先进行条件判断)【双重检查锁DCL】写法——优化3.0版(仍有问题)
public class lazyman {
private static lazyman instance;
private lazyman(){
}
public static lazyman getInstance(){
if(instance==null){
synchronized (lazyman.class){//类对象,全局只有一个,每一个类,jvm都会唯一创建一个对象来表示它
if(instance==null){
instance=new lazyman();
}
}
}
return instance;
}
}
4.给对象加volatile解决内存可见性问题与指令重排序问题写法——优化4.0版(不优雅但完整)
//4.加volatile修饰对象
public class lazyman {
private static volatile lazyman instance;
private lazyman(){
}
public static lazyman getInstance(){
if(instance==null){
synchronized (lazyman.class){
if(instance==null){
instance=new lazyman();
}
}
}
return instance;
}
}
经历以上几步,懒汉思想的写法已经很好了,但还有没有和饿汉一样天生线程安全的优雅写法呢?有,它就是使用静态内部类【在静态内部类中创建静态的常量对象,有方法调用这个内部类时,静态内部类才加载,创建对象,并且只加载一次也只创建一次对象】。写法如下:
//5.使用静态内部类
public class lazyman {
private static class node {
private static final lazyman instance=new lazyman();
}
public lazyman(){
}
public static lazyman getInstance(){
return node.instance;
}
}
以上饿汉思想与懒汉思想的写法,构成了单例模式的所有写法。
更多推荐



所有评论(0)