设计模式-单例模型
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();
}
}
}