一句话总结
保证独一无二
实质
一个类只有一个实例
三步骤
1 私有的构造函数
2 私有的的成员变量:记录这个单实例
3 公有的getter函数:没有实例时创建它;已有实例则返回该实例
场景
配置文件,IOC容器,日历,监控系统
技术方案:
1 饿汉式:在加载类时创建单例对象,优点:绝对的线程安全,缺点:浪费内存
public class Hungry {
private Hungry(){}
/**
* 类加载顺序:
* 先静态、后动态
* 先属性、后方法
* 先上后下
*/
private static final Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
/**
* 测试线程安全
*/
public class ThreadSafeTest {
public static void main(String[] args) {
int count = 200;
CountDownLatch latch = new CountDownLatch(count);
long start = System.currentTimeMillis();
for (int i = 0; i < count;i ++) {
new Thread(){
@Override
public void run() {
try{
try {
latch.await();
}catch(Exception e){
e.printStackTrace();
}
Object obj = LazyOne.getInstance();
System.out.println(System.currentTimeMillis() + ":" + obj);
}catch (Exception e){
e.printStackTrace();
}
}
}.start();
latch.countDown();
}
long end = System.currentTimeMillis();
System.out.println("总耗时:" + (end - start));
}
}
2 懒汉式:在第一次调用时实现,为了保证线程安全,有以下两种实现方式
2.1 通过synchronized加锁实现,缺点是耗时长
/**
* 通过synchronized保证线程安全
*/
public class LazyTwo {
private LazyTwo(){}
private static LazyTwo lazy = null;
public static synchronized LazyTwo getInstance(){
if(lazy == null){
lazy = new LazyTwo();
}
return lazy;
}
}
2.2 通过内部类实现,目前最优秀的实现单例的方式
/**
* 通过内部类实现单例,解决了饿汉式的内存浪费和synchronized的性能
*/
public class LazyThree {
private boolean initialized = false;
//默认使用LazyThree的时候,会先初始化内部类
//如果没使用的话,内部类是不加载的
private LazyThree(){
synchronized (LazyThree.class){//解决通过反射来创建单例,一般不需要考虑
if(initialized == false){
initialized = !initialized;
}else{
throw new RuntimeException("单例已被侵犯");
}
}
}
public static final LazyThree getInstance(){
//在返回结果以前,一定会先加载内部类
return LazyHolder.LAZY;
}
//默认不加载
private static class LazyHolder{
private static final LazyThree LAZY = new LazyThree();
}
}
3 注册登记式:spring使用的方式,下面的例子存在线程安全
//Spring中的做法,就是用这种注册式单例
public class BeanFactory {
private BeanFactory(){}
//线程安全
private static Map ioc = new ConcurrentHashMap();
public static Object getBean(String className){
if(!ioc.containsKey(className)){
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className,obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}else{
return ioc.get(className);
}
}
}
4 枚举式:枚举的实质是继承Enum的类,枚举元素是类中的常量,所有可以保证线程安全。
public enum RegiterEnum {
INSTANCE,BLACK,WHITE;
public void getInstance(){}
}
5 序列化和反序列化保证单例:通过重写readResolve()来实现
public class Seriable implements Serializable {
public final static Seriable INSTANCE = new Seriable();
private Seriable(){}
public static Seriable getInstance(){
return INSTANCE;
}
/**
* 重写此方法是为了解决反序列化默认创建对象
*/
private Object readResolve(){
return INSTANCE;
}
}
/**
* 单例方法的测试类
*/
public class SeriableTest {
public static void main(String[] args) {
Seriable s1 = null;
Seriable s2 = Seriable.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("Seriable.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("Seriable.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (Seriable)ois.readObject();
ois.close();
System.out.println(s1 == s2);//true
} catch (Exception e) {
e.printStackTrace();
}
}
}