单例模式
概述
单例模式是结构最简单的设计模式,在他的核心结构中只包含一个被称为单例的特殊类,通过单例模式可以确保系统中一个类只有一个而且改实列易于被外界访问,从而方便对实例个数进行控制,节约系统资源
确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一的实例
在单例模式实现过程中需要注意的是:
- 单例类构造函数的可见性为private
- 提供一个类型为自身的静态私有成员变量
- 提供一个公有的静态工厂方法
单例模式结构
饿汉式单例与懒汉式单例
饿汉式
当类被加载时,静态变量instance会被初始化,此时类得私有构造函数会被调用,单例类得唯一实例将被创建
懒汉式
懒汉式的单例类在第一次被引用时将自己实例化,在懒汉式单例加载时不会将自己实例化,
为了避免多个线程同时调用getInstance()方法,可以使用 关键字synchronized 虽然解决了线程安全问题,但是每次调用getInstance()时都需要进行线程锁定判断,在多线程高并发访问环境中将会导致系统得性能大大降低,升级为锁定代码块,但是又会出现在某一个瞬间线程A 和线程B 同时调用getInstance()方法,B线程在排队中,当A 线程执行完成后,B 线程并不知道已经创建了实例对象,将继续创建B 的实例对象,导致产生多个单例对象,违背了单例模式的设计思想, 使用双重检查锁定
饿汉式和懒汉式的对比
饿汉式类加载时加载不需要考虑多线程问题,可以确保唯一性,从调用速度和反应时间来讲是优于懒汉式的,但是由于不管是否使用该实例系统加载时都需要创建饿汉式单例对象会消耗资源和加载时间长
懒汉式
无须占用系统资源,延迟加载,需要处理多线程问题,
饿汉式单例模式
public class EagerSingleton {
private List serverList = null ;
//饿汉单例模式 当类被加载时,静态变量instance 会被初始化,此时类的私有构造函数会被调用, 单例类的唯一实例被创建
private static final EagerSingleton instance = new EagerSingleton();
public EagerSingleton() {
serverList = new ArrayList<>();
// TODO Auto-generated constructor stub
}
public static EagerSingleton getInstance() {
return instance;
}
//增加服务器
public void addServer(String server) {
serverList.add(server);
}
public String getServer() {
Random random = new Random();
int i = random.nextInt(serverList.size());
return (String) serverList.get(i);
}
}
public class Client {
public static void main(String[] args) {
EagerSingleton balancer1,balancer2,balancer3,balancer4;
balancer1 = EagerSingleton.getInstance();
balancer2 = EagerSingleton.getInstance();
balancer3 = EagerSingleton.getInstance();
balancer4 = EagerSingleton.getInstance();
if(balancer1 == balancer2
&& balancer2 == balancer3
&& balancer3 == balancer4) {
System.out.println("服务器负载均衡器 具有唯一性");
}
//增加服务器
balancer1.addServer("server1");
balancer1.addServer("server2");
balancer1.addServer("server3");
balancer1.addServer("server4");
for(int i=0;i<10;i++) {
String server = balancer2.getServer();
System.out.println(server);
}
}
}
懒汉式单例模式
public class LazySingleton {
private List serverList = null ;
//懒汉单例模式
private volatile static LazySingleton instance = null;
public LazySingleton() {
serverList = new ArrayList<>();
// TODO Auto-generated constructor stub
}
public static LazySingleton getInstance() {
if(instance == null) {
synchronized(LazySingleton.class) {
if(instance == null ) {
System.out.println("nei");
instance = new LazySingleton();
}
}
}
return instance;
}
//增加服务器
public void addServer(String server) {
serverList.add(server);
}
public String getServer() {
Random random = new Random();
int i = random.nextInt(serverList.size());
return (String) serverList.get(i);
}
}
优缺点
优点
主要在于提供了对唯一实例的受控访问并可以节约系统资源
缺点
主要在于因为缺少抽象层而难以扩展,并且单例类责任比较重,将太多功能耦合在一起
使用环境
系统只需要一个实例对象,客户调用类的单个实例只允许使用一个公共的访问点