设计模式来源于生活,是生活的升华。
什么是单例模式
一种常见的设计模式,主要的写法:懒汉式,饿汉式、注册登记式。单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
懒汉式:看上去比较懒,不到火烧眉毛的时候,不着急实例化,需要用的时候才实例化。
饿汉式:不管用不用,都先实例化。
注册登记式:有一个容器装着所有实例,在实例产生前先检查一下容器看有没有,如果有就直接取出来,如果没有就先new一个放进去,然后留给后面用,如:Spring IOC容器。
哪里用到单例模式?
Spring中用的最多,在spring context,Factory中都有体现,bean默认是单例,配置文件是单例(多例了不知道以谁为准)。全局性的东西要用单例。如:Tomcat中的servletContext、application。
java加载的一些规则
1.从上往下加载
2.先静态后动态(静态块和static关键字修饰在实例化以前分配内存空间)
3.先属性后方法,成员变量不能定义在方法中,只能定义在class下。
七种创建方式
懒汉式
类在实例化前先声明静态属性singlenton,然后就可以共享这个实例了,构造方法的私有化使得类不能new出来,因此提供静态的获取实例方法。
package com.test;
public class Singlenton1 {
//1.将构造方法私有化
private Singlenton1(){
}
//2.声明静态变量保存单例的引用
private static Singlenton1 singlenton=null;
//3.提供静态方法来获取单例的引用,这时遇到多个线程同时抢占cpu资源,该方法就会被new两次,两次的值不一样
public static Singlenton1 getSinglenton(){
if(singlenton==null){
singlenton=new Singlenton1();
}
return singlenton;
}
}
懒汉式单例保证线程安全,加同步锁。
package com.test;
public class Singlenton1 {
private Singlenton1(){
}
private static Singlenton1 singlenton=null;
//提供静态方法来获取单例的引用,为保证多线程环境下正确访问,给该方法加同步锁synchronized,保证了方法不能调用两次,但是new可以两次。
public static synchronized Singlenton1 getSinglenton(){
if(singlenton==null){
singlenton=new Singlenton1();
}
return singlenton;
}
}
懒汉式双重锁检查
package com.test;
public class Singlenton1 {
private Singlenton1(){
}
private static Singlenton1 singlenton=null;
//提供静态方法来获取单例的引用,为保证多线程环境下的另一种实现方式,双重锁检查
public static Singlenton1 getSinglenton(){
if(singlenton==null){
synchronized (Singlenton1.class) {
if(singlenton==null)
singlenton=new Singlenton1();
}
}
return singlenton;
}
}
饿汉式
不会出现线程安全的问题,不管有多个线程,在加载之前已经完成了初始化,但是当这个实例一直没有被使用时,空间就会被浪费。
package com.test;
public class Singlenton2 {
private Singlenton2(){
}
//声明静态变量,在类实例化之前就初始化变量,将对象引用保存
private static Singlenton2 singlenton=new Singlenton2();
//提供静态方法来获取实例
public static Singlenton2 getSinglenton(){
return singlenton;
}
}
懒汉式
最牛逼的方法,没有一个字母是多余的,也没有一个关键字是多余的。
内部类的初始化,需要依赖主类,给内部类加static关键字,即当我们JVM加载主类时内部类也被加载进来,但是还没实例化,需要等主类先实例化后内部类才开始实例化
package com.test;
public class Singleton {
//构造方法私有化
private Singleton(){}
private static class InnerClass{
//final为了防止内部将这个属性值覆盖掉
private static final Singleton INSTANCE=new Singleton();
}
//加final是为了防止子类重写父类方法
public static final Singleton getImstance(){
return InnerClass.INSTANCE;
}
}
枚举式不常用
package com.test;
public enum Singleton3 {
INSTANCE;
public void getInstance(){
}
}
注册登记式
spring最顶层容器Map,每个class对应一个id(唯一),容器启动后每个class实例化放在map中,根据查,若为null新建,有的话直接拿来用。
package com.test;
import java.util.HashMap;
import java.util.Map;
public class Singleton4 {
//类似Spring里面的方法,将类名注册,下次从里面直接获取。
private static Map<String, Singleton4> map=new HashMap<String, Singleton4>();
static{
Singleton4 single=new Singleton4();
map.put(single.getClass().getName(), single);
}
//保护的默认构造
protected Singleton4(){}
//静态工厂方法,返回此类的唯一实例
public static Singleton4 getInstance(String name){
if(name==null){
name=Singleton4.class.getName();
}
if(map.get(name)==null){
try {
map.put(name, (Singleton4) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
}
线程并发访问单例
访问最牛逼的方式创建的
package com.test;
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) {
//启动多个线程同时去抢cpu
int count=100;
//发令枪:多个线程同时发生。
CountDownLatch latch=new CountDownLatch(count);
for (int i = 0; i < count; i++) {
new Thread(){
public void run(){
Singleton.getImstance();
System.out.println(System.currentTimeMillis()+""+Singleton.getImstance());
}
}.start();
latch.countDown();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果,从图中可以看出在并发的情况下也是同一个实例化。