单例模式
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供整个实例
特点:
- 单例类只有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这个实例
为什么使用单例模式?
当我们需要确保某个类只要一个对象,或者创建一个类需要消耗的资源过多。
数据库连接实例主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为用单例模式来维护,就可以大大降低这种损耗。
手写单例模式
第一种单例模式(饿汉式(静态常量))
package com.singleton.type1;
/**
* @ClassName singletonTest1
* @Author Tian
* @Date 2020-06-12-14:22
* @Description 饿汉式(静态变量)
**/
class Singleton{
//1.构造器私有化
private Singleton(){
}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//3.提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
public class singletonTest1 {
public static void main(String[] args) {
//测试
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//创建的两个对象是相同的 true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
这种写法比较简单,在类加载的时候完成实例化,避免了线程同步问题,但是可能造成内存浪费
第二种单例模式(懒汉式(线程不安全))
package com.singleton.type3;
/**
* @ClassName SingletonTset3
* @Author Tian
* @Date 2020-06-12-14:39
* @Description 懒汉式(线程不安全)
**/
class Singleton{
private static Singleton instance;
private Singleton(){
}
//提供静态的公共方法,当使用该方法时,才去创建instance
public static Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
public class SingletonTest3 {
public static void main(String[] args) {
//测试
System.out.println("懒汉式线程不安全");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
只能在单线程下使用,多线程中不可!
第三种单例模式(懒汉式(线程安全)(同步方法))
package com.singleton.type4;
/**
* @ClassName SingletonTset3
* @Author Tian
* @Date 2020-06-12-14:39
* @Description 懒汉式(线程安全 同步方法)
**/
class Singleton{
private static Singleton instance;
private Singleton(){
}
//提供静态的公共方法,加入了同步方法,解决线程安全问题,当使用该方法时,才去创建instance
public static synchronized Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
public class SingletonTest4{
public static void main(String[] args) {
//测试
System.out.println("懒汉式线程安全");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
不推荐,效率太低,每个线程获取类的实例都要执行getInstance()方法。而这个方法只需要执行一次,其余的直接return instance就ok了
(懒汉式(线程安全)(同步代码块)
package com.singleton.type4;
/**
* @ClassName SingletonTset3
* @Author Tian
* @Date 2020-06-12-14:39
* @Description 懒汉式(线程安全 同步代码块)
**/
class Singleton{
private static Singleton instance;
private Singleton(){
}
//提供静态的公共方法,加入了同步方法,解决线程安全问题,当使用该方法时,才去创建instance
public static Singleton getInstance(){
if (instance == null){
synchronized (Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
public class SingletonTest4{
public static void main(String[] args) {
//测试
System.out.println("懒汉式线程安全");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
**这种方式是对上面的同步方法实现方式的改建,改为同步代码块,但是依旧不能解决线程同步问题,因为一旦有线程进入到if(instace == null)
**语句中 还未来的及判断,另一个线程也通过了这个语句,那么久会产生多个实例。
第四种单例模式(双重检查锁定Double-check)
package com.singleton.type5;
/**
* @ClassName SingletonTset3
* @Author Tian
* @Date 2020-06-12-14:39
* @Description 懒汉式(双重检查Double-check)
**/
class Singleton{
private static volatile Singleton instance;
private Singleton(){
}
//提供静态的公共方法,加入了双重检查代码,解决线程安全问题,同时保证了效率。
public static Singleton getInstance(){
if (instance == null){
synchronized (Singleton.class){
if(instance == null)
instance = new Singleton();
}
}
return instance;
}
}
public class SingletonTest5{
public static void main(String[] args) {
//测试
System.out.println("双重检查锁定");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
线程安全,延迟加载,效率高,实际中推荐
第五种单例模式(静态内部类)
package com.singleton.type6;
/**
* @ClassName SingletonTset3
* @Author Tian
* @Date 2020-06-12-14:39
* @Description 懒汉式(静态内部类)
**/
class Singleton{
private Singleton(){
}
//静态内部类,该类中有一个静态属性Singleton
private static class SingletonInstance{
private static final Singleton instance = new Singleton();
}
//提供静态公有方法,直接返回return SingletonInstance.instance;
public static Singleton getInstance(){
return SingletonInstance.instance;
}
}
public class SingletonTest6{
public static void main(String[] args) {
//测试
System.out.println("静态内部类单例模式");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
使用了JVM的类加载机制,静态内部类在类加载时并没有加载,而是随着方法的调用而加载,从而完成Singleton的实例化,在加载时只加载一份,所以是线程安全的
第五种单例模式(枚举)
package com.singleton.type7;
/**
* @ClassName SingletonTest7
* @Author Tian
* @Date 2020-06-12-15:22
* @Description 枚举实现单例模式
**/
public class SingletonTest7 {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;
System.out.println(instance == instance1);
}
}
enum Singleton{
INSTANCE;//属性
public void test(){
System.out.println("1");
}
}
不仅能够避免多线程同步的问题。而且还能防止反序列化重新创建对象