单例设计模式(饿汉式)(懒汉式)
单例设计模式:解决一个类在内存中只能存在一个对象
- 禁止其他程序创建该类对象 (将构造函数私有化)
- 为了让其他程序可以访问该对象,在本类创建唯一对象 (在类中new一个对象)
- 提供对外访问方法,让外部可以访问自定义的对象 (提供方法获取自定义对象)
应用场景:
- 统计当前在线人数(网站计数器):用一个全局对象来记录。
- 打印机(设备管理器):当有两台打印机,在输出同一个文件的时候只一台打印机进行输出。
- 数据库连接池(控制资源):一般是采用单例模式,因为数据库连接是一种连接数据库资源,不易频繁创建和销毁。
- 数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率的损耗还是非常昂贵的,因此用单例模式来维护,就可以大大降低这种损耗。
- 配置文件(共享数据),例如更改软件背景颜色,再次打开这软件背景颜色不变
饿汉式:
这种方式不会产生线程安全问题,因为在JVM中,对类的加载和类初始化,由虚拟机保证线程安全。
优点:没有加锁,执行效率会提高。
缺点:可能有时候不需要使用,会浪费内存,如果这个对象很大时,更会影响内存占用。
public class Single1 {
//饿汉式
//1,将构造函数私有化,外部就不可以创建对象
private Single1(){
}
//2,本类中自定义一个对象
private static Single1 s=new Single1();
//3,static修饰获取 S 对象,不能new对象,则不能使用对象调用此功能,所以需要使用类名调用,必须static
public static Single1 getInstance(){
return s;
}
}
懒汉式:
对象在方法被调用时才被创建/对象的延时加载
class Single2 {
//懒汉式
private Single2(){
}
private static Single2 s=null;
public static Single2 getInstance(){ //线程不安全写法
if (s==null){
s=new Single2();
}
return s;
}
}
public static synchronized Single2 getInstance(){ //懒汉式线程安全写法
if (s==null){
s=new Single2();
}
return s;
}
在多线程单例模式下,懒汉式容易出现问题;
两个线程都执行该对象
当线程A执行完 (s = =null) 后,没有继续执行,CPU切换线程B执行,B也执行了 (s= =null) 和(s=new Single2() ),创建了一个该类的对象. CPU切回到线程A.这时线程A也会执行 (s=new Single2() ).又创建了该类对象.导致了唯一性错误.
synchronized : 可以优化以上问题.当线程A执行getInstance() ,其他线程只能执行到synchronized (Single2.class) 前的程序
懒汉式优化(双重判空)
使用 synchronized 保证线程在创建对象的时候让其他线程阻塞
class Single2 {
//懒汉式优化
private Single2(){
}
private static Single2 s=null;
public static Single2 getInstance(){
if (s==null){
synchronized (Single2.class){
if (s==null){
s=new Single2();
}
}
} return s;
}
}
简单实例演示-----
class ColorSet{
private String Color="灰色";
public void setColor(String Color){ //设置颜色
this.Color=Color;
}
public String getColor(){ //获取颜色
return Color;
}
private ColorSet(){ //私有构造函数,禁止new对象
}
private static ColorSet c=new ColorSet(); //类中new唯一对象
public static ColorSet getNew(){ //获取唯一对象
return c;
}
}
public class DEMO {
public static void main(String[] args) {
ColorSet c1= ColorSet.getNew();
System.out.println("默认颜色= "+c1.getColor());
c1.setColor("蓝色");
System.out.println("修改后颜色="+c1.getColor());
ColorSet c2 =ColorSet.getNew();
System.out.println("c2颜色="+c1.getColor());
}