单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点
单例模式的实现
实现过程注意三点:
(1)单例类构造函数的可见性为private
(2)提供一个类型为自身的静态私有成员变量
(3)提供一个公有的静态工厂方法
饿汉式(在类加载时单例对象就已创建)
package singleton;
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return instance;
}
}
问题:会增加类加载时的负担
懒汉式(第一次使用时才加载)
package design.singleton;
/*
*
*@author:zzf
*@time:2020-12-18
*
*/
public class Singleton {
//静态私有变量
private static Singleton instance=null;
//私有构造函数
private Singleton(){}
//静态公有工厂方法,返回唯一实例
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
问题:存在线程不安全
解决:函数加锁,双重检查锁定
对方法加锁
package design.singleton;
/*
*
*@author:zzf
*@time:2020-12-18
*
*/
public class LazySingleton {
private static LazySingleton instance=null;
private LazySingleton(){}
synchronized public static LazySingleton getInstance(){
if(instance==null){
instance=new LazySingleton();
}
return instance;
}
}
问题:同步的范围是整个方法,范围太大,可以缩小
双重检查锁定
public 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;
}
}
对象创建分为三步
1、分配内存
2、初始化对象
3、对象指向刚分配的地址
真正执行时,JVM可能会为了效率对指令重排序,即执行完1后直接执行3,对出现对象未初始化,对象为null的情况
增加volatile是为了防止重排序
静态内部类实现单例模式(IoDH)
package design.singleton;
/*
*
*@author:zzf
*@time:2020-12-18
*
*/
public class Singleton {
private Singleton(){}
//静态内部类
private static class HolderClass{
private final static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return HolderClass.instance;
}
}
应用实例
某软件公司承接了一个服务器负载均衡(Load Balance)软件的开发工作,该软件运行在一台负载均衡服务器上,可以将并发访问和数据流量分发到服务器集群中的多台设备上进行并发处理,提高了系统的整体处理能力,缩短了响应时间。由于集群中的服务器需要动态删减,且客户端请求需要统一分发,因此需要确保负载均衡器的唯一性,只能有一个负载均衡器来负责服务器的管理和请求的分发,否则将会带来服务器状态的不一致以及请求分配冲突等问题。如何确保负载均衡器的唯一性是该软件成功的关键,试使用单例模式设计服务器负载均衡器。
代码实现
package design.singleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class LoadBalancer {
private static LoadBalancer instance=null;
private List serverList=null;
private LoadBalancer() {
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);
}
public String getServer() {
Random random=new Random();
int i=random.nextInt(serverList.size());
return (String) serverList.get(i);
}
}
客户端
package design.singleton;
/*
*
*@author:zzf
*@time:2020-12-18
*
*/
public class Client {
public static void main(String[] args) {
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("Server1");
balancer1.addServer("Server2");
balancer1.addServer("Server3");
balancer1.addServer("Server4");
// 模拟客户端请求的分发
for (int i = 0; i < 10; i++) {
String server = balancer1.getServer();
System.out.println("分发请求至服务器:" + server);
}
}
}
饿汉式实现
修改LoadBalancer
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class LoadBalancer {
private List serverList = null;
private static final LoadBalancer instance = new LoadBalancer();
private LoadBalancer() {
serverList = new ArrayList();
}
public static LoadBalancer getLoadBalancer() {
return instance;
}
public void addServer(String server) {
serverList.add(server);
}
public void removeServer(String server) {
serverList.remove(server);
}
public String getServer() {
Random random = new Random();
int i = random.nextInt(serverList.size());
return (String) serverList.get(i);
}
}
懒汉式实现
函数线程锁定
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class LoadBalancer {
private static LoadBalancer instance = null;
private List serverList = null;
private LoadBalancer() {
serverList = new ArrayList();
}
// 函数线程锁定
synchronized 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);
}
public String getServer() {
Random random = new Random();
int i = random.nextInt(serverList.size());
return (String) serverList.get(i);
}
}
双重检查锁定
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class LoadBalancer {
private volatile static LoadBalancer instance = null;
private List serverList = null;
private LoadBalancer() {
serverList = new ArrayList();
}
// 双重检查锁定
public static LoadBalancer getLoadBalancer() {
// 第一重判断
if (instance == null) {
// 锁定代码块
synchronized (LoadBalancer.class) {
// 第二重判断
if (instance == null) {
// 创建单例实例
instance = new LoadBalancer();
}
}
}
return instance;
}
public void addServer(String server) {
serverList.add(server);
}
public void removeServer(String server) {
serverList.remove(server);
}
public String getServer() {
Random random = new Random();
int i = random.nextInt(serverList.size());
return (String) serverList.get(i);
}
}
IoDH(静态内部类)
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class LoadBalancer {
private List serverList = null;
private LoadBalancer() {
serverList = new ArrayList();
}
// IDH
private static class HolderClass {
private final static LoadBalancer instance = new LoadBalancer();
}
public static LoadBalancer getLoadBalancer() {
return HolderClass.instance;
}
public void addServer(String server) {
serverList.add(server);
}
public void removeServer(String server) {
serverList.remove(server);
}
public String getServer() {
Random random = new Random();
int i = random.nextInt(serverList.size());
return (String) serverList.get(i);
}
}
实例2
使用单例模式设计一个多文档窗口(使用JDesktopPane和JInternalFrame来实现),要求在主窗体中某个内部子窗体只能实例化一次,即一次只能弹出一个相同的子窗体
单例类
package design.singleton.test;
/*
*
*@author:zzf
*@time:2020-12-18
*
*/
import javax.swing.JInternalFrame;
public class JInternalFrameDemo extends JInternalFrame {
private static JInternalFrameDemo jInternalFrameDemo;
public JInternalFrameDemo(String title, boolean resizable, boolean closable, boolean maximizable,
boolean iconifiable) {
super(title, resizable, closable, maximizable, iconifiable);
}
public static JInternalFrameDemo getJInternalFrameDemo(String title, boolean resizable, boolean closable,
boolean maximizable, boolean iconifiable) {
if (jInternalFrameDemo == null) {
jInternalFrameDemo = new JInternalFrameDemo(title, resizable, closable, maximizable, iconifiable);
}
return jInternalFrameDemo;
}
}
窗体类
package design.singleton.test;
/*
*
*@author:zzf
*@time:2020-12-18
*
*/
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyVetoException;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
public class CreateJInternalFrame extends JFrame implements ActionListener {
private static JInternalFrameDemo jInternalFrameDemo;
Container contentPane = this.getContentPane();
public CreateJInternalFrame() {
super("主窗体");
contentPane.setLayout(new BorderLayout());
JButton button = new JButton("单击创建一个内部窗体");
button.addActionListener(this);
contentPane.add(button, BorderLayout.SOUTH);
this.setSize(new Dimension(500, 500));
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
@Override
public void actionPerformed(ActionEvent e) {
// System.out.println("come on");
jInternalFrameDemo = JInternalFrameDemo.getJInternalFrameDemo("子窗体", true, true, true, true);
jInternalFrameDemo.setSize(new Dimension(300, 300));
jInternalFrameDemo.setVisible(true);
JDesktopPane jDesktopPane = new JDesktopPane();
contentPane.add(jDesktopPane);
jDesktopPane.add(jInternalFrameDemo);
try {
jInternalFrameDemo.setSelected(true);
} catch (PropertyVetoException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}
}
}
客户端
package design.singleton.test;
/*
*
*@author:zzf
*@time:2020-12-18
*
*/
public class Client {
public static void main(String[] args) {
new CreateJInternalFrame();
}
}
单例模式优缺点
优点:
(1)提供了对唯一实例的受控访问,可以严格控制客户怎样及何时访问它
(2)节约系统资源,提高系统性能
(3)允许可变数目的实例
缺点:
(1)没有抽象层,扩展困难
(2)单例类职责过重,在一定程度上违反单一职责原则
(3)如果实例化的共享对象长时间不被利用,会被拥有自动垃圾回收技术的环境销毁并回收
适用环境
(1)系统只需要一个实例对象
(2)客户调用类的单个实例只允许使用一个公共访问点
参考
Java设计模式