单例设计模式: 保证一个类在内存中只有一个对象!!
模式:
**模式就是解决一类问题的固定步骤。
思路:
- 构造放私有化
- 此类内部要构建一个实例(对象)
- 外界可通过此类的静态方法访问此类实例
实现思想
- 类加载时构建对象
- 访问类的静态方法时构建对象
优缺点:
饿汉:(小对象,经常用)
优点:无需考虑线程安全,执行效率比较高
缺点:类加载时创建对象,对象比较大就会占用内存。
懒汉:(大对象,稀少用)
优点:对象何时需要何时创建,减少内存的消耗。
缺点:需要考虑线程安全,效率会比较低。
class Singleton01{//懒汉式单例
private Singleton01(){}
private static Singleton01 instance;
/**外界通过此方法获得对象*/
public synchronized static Singleton01 getInstance(){
if(instance==null){
instance=new Singleton01();
}
return instance;
}//多个线程访问此方法时有问题吗?
//会获得多个对象吗?可能
}
class Singleton02{//饿汉单例
//private int[] a=new int[Integer.MAX_VALUE];
private Singleton02(){}
/**类加载时创建对象*/
private static Singleton02 instance
=new Singleton02();
public static Singleton02 getInstance(){
return instance;
}
public static void display(){}
public void show(){}
}
public class SingletonDemo1{
public static void main(String[] args) {
Singleton02.display();//类加载时创建对象
}
}
改进单例:
类加载时无需构建对象。需要时创建对象。
需要对象时还要有对象,而且不需要考虑线程安全问题。
class Singletion03{
private Singletion03(){
}
//此类设为静态的原因:
//因为要在静态方法中去访问这个类。静态方法内部只能访问静态成员。
private static class Inner01{
//此变量设为静态的原因:
//通过类名直接访问,并保证初始化一次
private static Singleton03 instance=
new Singleton03();
}
//需要时创建对象,无需考虑线程安全。
public static Singletion03 getInstance(){
return Inner01.instance;
}
}
枚举单例(饿汉)
- 类加载时创建对象。
//单例模式的枚举实现
enum Singleton04{
instance ;//此对象类加载时创建
private Singleton04(){
System.out.println("输出说明类加载时创建对象了");
}
//构造方法默认是私有的
//private Singleton04{}
public void display{}
}
public class SingletonDemo1{
public static void main(String[] args){
//调用枚举类方法
Singleton04.instance.display();
}
}
线程内部单例
- 在每个线程内部,某个类的实例在某一时刻只有一份。
使用场景:
- 例如在打电话的时候,当前线路是私有的,不是公有。也就是每个线程一个连接对象。
实现思想:
- 当对象创建以后可以将对象绑定到当前线程上。
借助实现:
- 借助ThreadLocal,此类中提供了一个set方法能够将对象绑定到当前线程。也可通过get方法从当前线程获取对象。
注:ThreadLocal内置一个Map。
/**
* 设计思想:
* 此类的实例每个线程只有一份
*/
//Singleton05的对象,在多线程之间不能共享,且要求每个线程此类的对象只有一个。借助ThreadLocal。ThreadLocal底层内置map存储键值对。
class Singleton05{
}
//工具类,内置方法获得Singleton05类的实例
class SingletonUtil{
private static ThreadLocal<Singleton05> td=
new ThreadLocal<Singleton05>();
//ThreadLocal对象底层会有一个map对象。
public static Singleton05 getInstance(){
Singleton05 s= td.get();//获得当前线程的Singleton05对象
//td.get();方法底层调用了map.get(Thread.currentThread);
//Thread.currentThread:当前线程
if(s==null){
s=new Singleton05();
td.set(s);//将对象绑定到当前线程
//td.set(s);方法底层调用了map.put(Thread.currentThread,s)
}
return s;
}
}
public class SingletonDemo2 {
public static void main(String[] args) {
//起线程
new Thread(){
public void run() {
for(int i=0;i<4;i++){
//打印
System.out.println("Thread01:"+SingletonUtil.getInstance());
}
};
}.start();
//起线程
new Thread(){
public void run() {
for(int i=0;i<4;i++){
//打印
System.out.println("Thread02:"+SingletonUtil.getInstance());
}
};
}.start();
//注:线程启动完毕之后构建对象,对象构建完之后,没有使用,对象销毁了
//再启动一个新的线程,又构建了一个新的对象,那个对象可能恰好用的是原先的地址空间
}
}