单例模式
1、动机
为了节约系统资源,确保系统中某个类只有唯一一个实例,无法再创建一个同类型的其他对象。 ---- windows的任务管理器。
2、单例模式
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
1、私有构造方法
2、内部创建static、private修饰。(static修饰为了将数据存储在全局作用区 使之全局可用)(静态方法只能访问静态的方法和变量。 非静态的方法和变量只有在对象初始化之后才能存在,才有可能被调用。而静态的方法可以通过类直接调用,不需要对象的存在。所以不可能在静态方法里调用非静态的东西。)
3、增加static、public方法对外提供。
class TaskManager
{
private static TaskManager tm = null;
private TaskManager() {……} //初始化窗口
public void displayProcesses() {……} //显示进程
public void displayServices() {……} //显示服务
public static TaskManager getInstance()
{
if (tm == null)
{
tm = new TaskManager();
}
return tm;
}
……
}
单例结构图
负载均衡结构图
import java.util.*;
//负载均衡器LoadBalancer:单例类,真实环境下该类将非常复杂,包括大量初始化的工作和业务方法,考虑到代码的可读性和易理解性,只列出部分与模式相关的核心代码
class LoadBalancer {
//私有静态成员变量,存储唯一实例
private static LoadBalancer instance = null;
//服务器集合
private List serverList = null;
//私有构造函数
private LoadBalancer() {
//增删较多,使用linkedList
// arrayList不是线程安全的,可使用vector、加synchronized、Collections.synchronizedList()或使用CopyOnWriteArrayList。
serverList = new ArrayList();
}
//公有静态成员方法,返回唯一实例
public static LoadBalancer getLoadBalancer() {
if (instance == null) {
instance = new LoadBalancer();
}
return instance;
}
//增加服务器
public void addServer(String server) {
serverList.add(server);
}
//删除服务器
public void removeServer(String server) {
serverList.remove(server);
}
//使用Random类随机获取服务器
public String getServer() {
Random random = new Random();
int i = random.nextInt(serverList.size());
return (String)serverList.get(i);
}
}
class Client {
public static void main(String args[]) {
//创建四个LoadBalancer对象
LoadBalancer balancer1,balancer2,balancer3,balancer4;
balancer1 = LoadBalancer.getLoadBalancer();
balancer2 = LoadBalancer.getLoadBalancer();
balancer3 = LoadBalancer.getLoadBalancer();
balancer4 = LoadBalancer.getLoadBalancer();
//判断服务器负载均衡器是否相同
if (balancer1 == balancer2 && balancer2 == balancer3 && balancer3 == balancer4) {
System.out.println("服务器负载均衡器具有唯一性!");
}
//增加服务器
balancer1.addServer("Server 1");
balancer1.addServer("Server 2");
balancer1.addServer("Server 3");
balancer1.addServer("Server 4");
//模拟客户端请求的分发
for (int i = 0; i < 10; i++) {
String server = balancer1.getServer();
System.out.println("分发请求至服务器: " + server);
}
}
}
3、饿汉模式和懒汉模式
在定义静态变量时实例化单例类,因此在类加载的时候就已经创建了单例对象。
延迟加载:在类加载时并不自行实例化。
双重检查锁定、volatile修饰符:线程安全,又不影响系统性能。
该代码只能在JDK 1.5及以上版本中才能正确执行。由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,因此即使使用双重检查锁定来实现单例模式也不是一种完美的实现方式。
class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton() { }
public static LazySingleton getInstance() {
//第一重判断
if (instance == null) {
//锁定代码块
synchronized (LazySingleton.class) {
//第二重判断
if (instance == null) {
instance = new LazySingleton(); //创建单例实例
}
}
}
return instance;
}
}
4、静态内部类(IoDH)
既可以实现延迟加载(节约系统资源),又可以保证线程安全,不影响系统性能
//Initialization on Demand Holder
class Singleton {
private Singleton() {
}
//只有将某个内部类修饰为静态类,然后才能够在这个类中定义静态的成员变量与成员方法
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
}
5、总结
主要优点
1、提供了对唯一实例的受控访问。
2、节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3、允许可变数目的实例,多例模式。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例,既节省系统资源,又解决了单例单例对象共享过多有损性能的问题。
package com.pocketdigi.patterns.multition;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 多例模式
*/
public class Multition {
String name;
// 实例数
private static final int MAX_INSTANCE = 2;
private static List instanceList = new ArrayList(MAX_INSTANCE);
private static List instanceInfoList = new ArrayList(MAX_INSTANCE);
static {
for (int i = 0; i < MAX_INSTANCE; i++) {
String n1 = "name" + i;
Multition m1 = new Multition(n1);
instanceList.add(m1);
}
}
/**
* 随机返回一个实例
*/
public static Multition getInstance() {
Random r = new Random();
return instanceList.get(r.nextInt(MAX_INSTANCE));
}
/**
* 返回指定的实例
* @param name
* @return
*/
public static Multition getInstance(String name) {
int index = instanceInfoList.indexOf(name);
if (index >= 0)
return instanceList.get(instanceInfoList.indexOf(name));
return null;
}
private Multition(String name) {
this.name = name;
instanceInfoList.add(name);
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name;
}
}
主要缺点
1、没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。(静态成员变量,地址在静态存储区中,不会被回收!!!)
适用场景
1、系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
2、客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。