版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HZMand1/article/details/83818303
Singleton 单例模式:
有些对象只需要一个,比如:线程池、缓存、对话框、处理偏好设置和注册表的对象、日志对象,充当打印机、显卡等设备的驱动程序的对象。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
应用实例:
1、要求生产唯一序列号。
2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
4、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来;
5、创建的一个对象需要消耗的资源过多,比如数据库的连接等。
懒汉式
以下代码是懒汉式的线程安全 和不安全的 实现 和验证:
public class SingletonTest {
public static void main(String[] args) {
SingletonTest singletonTest = new SingletonTest();
// esay singleton
// singletonTest.easySingleton();
// singleton one false 单线程 true 是多线程
// singletonTest.singletonOneTest(true);
// singleton two
singletonTest.singletonOneTwo();
}
public void easySingleton() {
// 不合法的构造函数
// 编译时错误:构造函数 SingleObject() 是不可见的
// SingleObject object = new SingleObject();
// 获取唯一可用的对象
SingletonObject object = SingletonObject.getInstance();
// 显示消息
object.test();
}
public void singletonOneTest(boolean isMultipe) {
try {
// 单线程
if (!isMultipe) {
Thread thread = new ThreadTest();
thread.start();
} else {
for (int i = 0; i < 200; i++) {
Thread t4 = new ThreadTest();
t4.start();
}
}
} catch (Exception e) {
System.out.println("线程异常了");
}
}
public void singletonOneTwo() {
for (int i = 0; i < 200; i++) {
Thread t4 = new ThreadTwoTest();
t4.start();
}
}
}
class ThreadTest extends Thread {
private static int num = 0;
public ThreadTest(){
num++;
}
public void run() {
try {
System.out.println("创建的第"+num+"个线程");
SingletonOne.getInstance();
} catch (Exception e) {
System.out.println("线程运行中断异常");
}
}
}
class ThreadTwoTest extends Thread {
private static int num = 0;
public ThreadTwoTest(){
num++;
}
public void run() {
try {
System.out.println("创建的第"+num+"个线程");
SingletonTwo.getIntance();
} catch (Exception e) {
System.out.println("线程运行中断异常");
}
}
}
/**
* 懒汉式,线程不安全
* 这种方式是最基本的实现方式;
* 这种实现最大的问题就是不支持多线程。
* 因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
* 这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
*/
public class SingletonOne {
private static int count = 0;
//定义对象
private static SingletonOne instance = null;
//私有化初始方法
private SingletonOne(){}
//实例化对象
public static SingletonOne getInstance(){
try {
if(null == instance) {
count ++;
System.out.println("Singleton 对象第 " + count + "次创建。");
instance = new SingletonOne();
}
} catch (Exception e) {
System.err.println("线程异常");
}
return instance;
}
}
/**
* 懒汉式,线程安全
* 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
* 优点:第一次调用才初始化,避免内存浪费
* 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
*/
public class SingletonTwo {
private static int count = 0;
private static SingletonTwo instance = null;
private SingletonTwo() {}
public static synchronized SingletonTwo getIntance() {
try {
if(null == instance) {
count ++;
System.out.println("Singleton 对象第 " + count + "次创建。");
instance = new SingletonTwo();
}
} catch (Exception e) {
System.err.println("线程异常");
}
return instance;
}
}
3.饿汉式
常用的 一种方式 ,在类加载的时候就开始初始化,所以这种方式很耗内存。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4、双检锁/双重校验锁(DCL,即 double-checked locking)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
总结下:
第一第二中肯定是建议少用的,第三种饿汉式 感觉还可以,第四种 双检锁 就更稳定点。