设计模式-单例模型

1 设计模式

jdbc–> 封装(static)–>properties(配置文件)–>数据源(dbcp)–>单例

2 什么是单例

创建型模式:创建对象
单例模型的核心:内存中只有唯一的一个对象

在普通类中,是容易产生多个对象的。比如:

public class A {

	public static void main(String[] args) {
		A a = new A();
		A b = new A();
		System.out.println(a == b);

	}

}

在这里插入图片描述
而单例模型,就是一种产生唯一对象的套路。

3 懒汉模式

/**
 * 单例模型-懒汉模式(一开始没有对象,在要使用的时候才创建对象)。
 * @author lenovo
 *
 */
public class Singleton {
	private static Singleton instance = null;// 类加载时初始化Singleton引用为null

	private Singleton() {// private将构造方法私有,使得外界无法随意造出一个对象。

	}

	/**
	 * 如果引用为空,则造出这个对象。若不为空,则直接返回instance
	 * 
	 * @return
	 */
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();// 将instance与创建出的对象绑定,之后instance不为空
		}

		return instance;
	}

}
/**
 * 测试单例模型-懒汉模式
 * 
 * @author lenovo
 *
 */
public class TestSingleton {

	public static void main(String[] args) {
		// 调用getInstance方法,尝试创建四次对象,查看是否是同一个对象
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		Singleton s3 = Singleton.getInstance();
		Singleton s4 = Singleton.getInstance();

		System.out.println(s1.hashCode());
		System.out.println(s2.hashCode());
		System.out.println(s3.hashCode());
		System.out.println(s4.hashCode());

	}

}

在这里插入图片描述

3.1 懒汉模式的线程安全问题

在多线程情况下可能会产生多个对象

public class ThreadSingletonTest {

	public static void main(String[] args) {
		Executor pool = Executors.newCachedThreadPool();

		for (int i = 0; i < 10; i++) {
			pool.execute(() -> {
				for (int j = 0; j < 10; j++) {
					System.out.println(Thread.currentThread().getName() + ":" + Singleton.getInstance().hashCode());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
		}

	}

}


在这里插入图片描述

造成线程不安全的原因在判断instance是否为空的地方

当一个线程判断出instance为空,开始创建对象,但是创建对象是需要时间的。若另一个线程此时也开始判断instance的值,此时instance还为空,就会再次创建一个对象,造成出现多个对象的情况。

3.2 线程安全解决方案

方案一:在getInstance()方法前加synchronized锁**

在这里插入图片描述

方案二:可重复锁

在这里插入图片描述

方案三:双重检查

public class SQLHelper {
	private static volatile SQLHelper instance;

	private SQLHelper() {

	}

	public static SQLHelper getInstance() {
		if (instance == null) {
			synchronized (SQLHelper.class) {
				if (instance == null) {
					instance = new SQLHelper();
				}
			}
		}
		return instance;
	}

}

4 饿汉模式

饿汉模式天生就是线程安全的

/**
 * 单例模型-饿汉模式:首先创建对象。
 * 
 * @author lenovo
 *
 */
public class Singleton2 {
	private static Singleton2 instance = new Singleton2();// jvm保证线程安全,static修饰只会创建一次对象

	private Singleton2() {

	}

	/**
	 * 获得对象的引用
	 * 
	 * @return
	 */
	public static Singleton2 getInstance() {
		return instance;
	}

}

5 用单例模型改造jdbc

/**
 * 引入单例模型
 * 
 * @author lenovo
 *
 */

public class DruidUtil2 {
	private static DruidUtil2 instance = null;
	private static final ReentrantLock lock = new ReentrantLock();

	private DruidUtil2() {
		init();
	}

	// 因为单例模型已经保证唯一对象,所以属性和方法不需要再使用static修饰
	private  Connection conn;
	private  PreparedStatement pst;
	private  ResultSet rs;
	private  DruidDataSource ds;// 数据源

	public static DruidUtil2 getInstance() {
		lock.lock();
		try {
			if (instance == null) {
				instance = new DruidUtil2();
			}
		} finally {
			lock.unlock();
		}
		return instance;
	}


	/**
	 * 创建数据源
	 */
	private  void init() {
		ds = new DruidDataSource();
		ds.configFromPropety(DruidConfig.prop());
		Logger.getLogger(DruidUtil2.class).debug("数据源配置成功:" + ds);
	}

	/**
	 * 在数据源中不再需要加载驱动了,因为数据源配置的时候就已经加载好了
	 */
//	public static void registDriver() {
//		try {
//			Class.forName(Config.getval("jdbc.driverclass"));
//		} catch (ClassNotFoundException e) {
//			Logger.getLogger(JdbcUtil.class).debug(e.getMessage());
//			e.printStackTrace();
//		}
//	}

	public  void connect() {
		try {
			conn = ds.getConnection();// 从数据源中拿到连接
		} catch (SQLException e) {
			Logger.getLogger(DruidUtil2.class).debug(e.getMessage());
			e.printStackTrace();
		}

	}

	public  void preparedStatement(String sql, Object... value) {
		try {
			pst = conn.prepareStatement(sql);
			for (int i = 0; i < value.length; i++) {
				pst.setObject(i + 1, value[i]);
			}

		} catch (SQLException e) {
			Logger.getLogger(DruidUtil2.class).debug(e.getMessage());
			e.printStackTrace();
		}
	}

	public  void executeUpate() {
		try {
			pst.executeUpdate();
		} catch (SQLException e) {
			Logger.getLogger(DruidUtil2.class).debug(e.getMessage());
			e.printStackTrace();
		}
	}

	public  ResultSet executeQuery() {
		try {
			rs = pst.executeQuery();
		} catch (SQLException e) {
			Logger.getLogger(DruidUtil2.class).debug(e.getMessage());
			e.printStackTrace();
		}
		return rs;
	}

	/**
	 * 关闭时,连接还回到数据源中
	 */
	public  void close() {
		try {
			if (rs != null) {
				rs.close();
			}
			if (pst != null) {
				pst.close();
			}
			if (conn != null) {
				conn.close();
			}
		} catch (SQLException e) {
			Logger.getLogger(DruidUtil2.class).debug(e.getMessage());
			e.printStackTrace();
		}
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值