单例模式
- 定义:保证一个类仅有一个实例,并提供一个全局访问点
- 类型:创建型
- 适用场景:想确保任何情况下都绝对只有一个实例
优点:
- 在内存里只有一个实例,减少了内存开销
- 可以避免对资源的多重占用
- 设置全局访问点,严格控制访问
- 缺点:没有接口,扩展困难
重点:
- 私有构造器
- 线程安全
- 延迟加载
- 序列化和反序列化安全
- 反射
- Double Check
静态内部类:
实用技能
- 反编译
- 内存原理
多线程Debug
相关设计模式
- 单例模式和工厂模式
- 单例模式和享元模式
代码示例
懒汉式
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {
}
/**
* synchronized(同步锁比较消耗资源)
* 加在静态方法上:锁的是类的class文件
* 加在普通方法上:锁的是堆内存中的对象
* @return
*/
public synchronized static LazySingleton getInstance(){//懒汉式
if(lazySingleton==null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
public class T implements Runnable{
@Override
public void run() {
LazySingleton lazySingleton = LazySingleton.getInstance();
System.out.println(Thread.currentThread()+" "+lazySingleton);
}
}
测试类:
public class Test {
public static void main(String[] args) {
// LazySingleton lazySingleton = LazySingleton.getInstance();
Thread t1 = new Thread(new T());
Thread t2 = new Thread(new T());
t1.start();
t2.start();
System.out.println("program end");
/* for (int i = 0; i < 10; i++) {
new Thread(new T()).start();
}*/
}
}
双重校验(改进):
public class LazyDoubleCheckSingleton {
// volatile多线程运行代码执行时重排序引起的问题(不允许重排序)
private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton;
private LazyDoubleCheckSingleton() {
}
public static LazyDoubleCheckSingleton getInstance(){//懒汉式
if(lazyDoubleCheckSingleton==null){
synchronized (LazyDoubleCheckSingleton.class){
if (lazyDoubleCheckSingleton==null){
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazyDoubleCheckSingleton;
}
}
基于类初始化的延迟加载解决方案:
/**
* 基于类初始化的延迟加载解决方案
*/
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {
}
private static class InnerClass{
private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance(){
return InnerClass.staticInnerClassSingleton;
}
}
public class T implements Runnable{
@Override
public void run() {
// LazySingleton lazySingleton = LazySingleton.getInstance();
// LazyDoubleCheckSingleton lazyDoubleCheckSingleton = LazyDoubleCheckSingleton.getInstance();
StaticInnerClassSingleton staticInnerClassSingleton = StaticInnerClassSingleton.getInstance();
System.out.println(Thread.currentThread()+" "+staticInnerClassSingleton);
}
}
饿汉式
public class HungrySingleton {//饿汉式
// 声明为final的变量必须在类加载时完成赋值
private static final HungrySingleton hungrySingleton;
static{
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
序列化破坏单例模式
public class HungrySingleton implements Serializable {//饿汉式
// 声明为final的变量必须在类加载时完成赋值
private static final HungrySingleton hungrySingleton;
static{
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
/**
* 如果不写该方法,反序列化时会通过反射创建一个新的对象并返回,与序列化的对象不是同一对象。
* 若写了该方法,则反序列化时,虽然通过反射创建一个新的对象,但会返回该对象。
* @return
*/
private Object readResolve(){
return hungrySingleton;
}
}
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
HungrySingleton instance = HungrySingleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
File file = new File("singleton_file");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
HungrySingleton newInstance = (HungrySingleton) ois.readObject();
System.out.println(instance);
System.out.println(newInstance);
System.out.println(instance==newInstance);
}
}
反射攻击解决方案
public class HungrySingleton implements Serializable {//饿汉式
// 声明为final的变量必须在类加载时完成赋值
private static final HungrySingleton hungrySingleton;
static{
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
if (hungrySingleton!=null){
throw new RuntimeException("单例构造器禁止反射调用");
}
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {
if (InnerClass.staticInnerClassSingleton!=null){
throw new RuntimeException("单例构造器禁止反射调用");
}
}
private static class InnerClass{
private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance(){
return InnerClass.staticInnerClassSingleton;
}
}
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {
if (lazySingleton!=null){
throw new RuntimeException("单例构造器禁止反射调用");
}
}
public synchronized static LazySingleton getInstance(){//懒汉式
if(lazySingleton==null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
测试类:
/**
*可以解决饿汉式的反射攻击,但没有解决懒汉式的反射攻击。
*/
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// Class hungrySingletonClass = HungrySingleton.class;
// Class objectClass = StaticInnerClassSingleton.class;
Class objectClass = LazySingleton.class;
Constructor constructor = objectClass.getDeclaredConstructor();
constructor.setAccessible(true);
// HungrySingleton instance = HungrySingleton.getInstance();
// HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
// StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
// StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();
LazySingleton newInstance = (LazySingleton) constructor.newInstance();
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
System.out.println(newInstance);
System.out.println(instance==newInstance);
}
}
Enum枚举单例
Jad:反编译工具
序列化
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// HungrySingleton instance = HungrySingleton.getInstance();
EnumInstance instance = EnumInstance.getInstance();
instance.setData(new Object());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
File file = new File("singleton_file");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
// HungrySingleton newInstance = (HungrySingleton) ois.readObject();
EnumInstance newInstance = (EnumInstance) ois.readObject();
System.out.println(instance.getData());
System.out.println(newInstance.getData());
System.out.println(instance.getData()==newInstance.getData());
}
}
反射
public enum EnumInstance {
INSTANCE{
protected void printTest(){
System.out.println("Feyl Print Test");
}
};
protected abstract void printTest();
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumInstance getInstance(){
return INSTANCE;
}
}
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class objectClass = EnumInstance.class;
Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
EnumInstance instance = (EnumInstance) constructor.newInstance("Feyl",666);
// EnumInstance instance = EnumInstance.getInstance();
// instance.printTest();
}
}
容器单例
public class ContainerSingleton {// 线程不安全
private static Map<String,Object> singletonMap = new HashMap<>();
private ContainerSingleton() {
}
public static void putInstance(String key, Object instance){
if (!StringUtils.isNotBlank(key)&&instance!=null){
if(!singletonMap.containsKey(key)){
singletonMap.put(key,instance);
}
}
}
public static Object getInstance(String key){
return singletonMap.get(key);
}
}
public class T implements Runnable{
@Override
public void run() {
ContainerSingleton.putInstance("object",new Object());
Object instance = ContainerSingleton.getInstance("object");
System.out.println(Thread.currentThread()+" "+instance);
}
}
测试类:
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Thread t1 = new Thread(new T());
Thread t2 = new Thread(new T());
t1.start();
t2.start();
System.out.println("program end");
}
}
TreadLocal线程单例
public class ThreadLocalInstance {
private static final ThreadLocal<ThreadLocalInstance> threadLocalInstance
= new ThreadLocal<ThreadLocalInstance>(){
@Override
protected ThreadLocalInstance initialValue() {
return new ThreadLocalInstance();
}
};
private ThreadLocalInstance(){ }
public static ThreadLocalInstance getInstance() {
return threadLocalInstance.get();
}
}
public class T implements Runnable{
@Override
public void run() {
ThreadLocalInstance instance = ThreadLocalInstance.getInstance();
System.out.println(Thread.currentThread()+" "+instance);
}
}
测试类同上
“以空间换时间”:ThreadLocal 为每一个线程创建一个空间,各个线程获取其独有的对象。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。