第6章 单例模式与多线程
标签: Java多线程编程
《Java多线程编程核心技术》 个人笔记
立即加载/“饿汉模式”
- 立即加载 就是使用类的时候已经将对象创建完毕,常见的实现方法就是直接new实例化,立即加载也称为“饿汉模式”
public class MyObject {
//非线程安全
private static MyObject myObject = new MyObject();
private MyObject() {
}
public static MyObject getInstance() {
return myObject;
}
}
延迟加载/“懒汉模式”
- 延迟加载 就是在调用get()方法时实例才被创建,常见的实例方法就是在get()方法中进行new实例化
public class MyObject {
private static MyObject myObject;
private MyObject() {
}
public static MyObject getInstance() {
//在多线程环境下,就会出现取出多个实例的情况
if (myObject != null) {
} else {
myObject = new MyObject();
}
return myObject;
}
}
但是在多线程环境下,前面“延迟加载”的代码完全是错误的,根本不能实现保持单例的状态。
- 解决方案:
- 声明synchronized关键字
- 尝试同步代码块
- 针对某些重要的代码进行单独的同步
- 使用DCL双检查锁机制
声明synchronized关键字
public class MyObject {
private static MyObject myObject;
private MyObject() {
}
// 设置同步方法效率太低了
// 整个方法被上锁
synchronized public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
//模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
myObject = new MyObject();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}
//-----------主函数--------------
public class Run {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}
尝试同步代码块
public static MyObject getInstance() {
try {
// 此种写法等同于:
// synchronized public static MyObject getInstance()
// 的写法,效率一样很低,全部代码被上锁
synchronized (MyObject.class) {
if (myObject != null) {
} else {
Thread.sleep(3000);
myObject = new MyObject();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
针对某些重要的代码进行单独的同步
public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
Thread.sleep(3000);
// 虽然效率提升,但是多线程环境下 非线程安全
synchronized (MyObject.class) {
myObject = new MyObject();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
使用DCL双检查锁机制
public class MyObject {
private volatile static MyObject myObject;
private MyObject() {
}
// 使用双检测机制来解决问题,既保证了不需要同步代码的异步执行性
//又保证了单例的效果
public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
Thread.sleep(3000);
synchronized (MyObject.class) {
if (myObject == null) { //双重检查
myObject = new MyObject();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
// 此版本称为双重检查 double-Check Locking
}
- 使用双重检查锁功能,成功解决了“懒汉模式”遇到多线程的问题。DCL也是大多数多线程结合单例模式使用的解决方案
使用静态内置类实现单例模式
- 除了DCL,也有其他方法可以解决多线程单例模式的非线程安全问题
public class MyObject {
// 内部类方式
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
}
private MyObject() {
}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
}
序列化与反序列化的单例模式实现
- 静态内置类可以达到线程安全问题,但如果遇到序列化对象,使用默认的方式运行得到的结果还是多例的
- 解决的方法就是在反序列化中使用readResoulve()方法
public class MyObject implements Serializable {
private static final long serialVersionUID = 888L;
private static class MyObjectHandler {
private static final MyObject myObject = new MyObject();
}
private MyObject() {
}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
protected Object readResolve() throws ObjectStreamException {
//反序列化用
System.out.println("调用了readResolve方法!");
return MyObjectHandler.myObject;
}
}
使用static代码块实现单例模式
- 静态代码块中的代码在使用类的时候就已经执行了,所以可以应用静态代码块的这个特性实现单例模式
public class MyObject {
private static MyObject instance = null;
private MyObject() {
}
static { //静态代码块
instance = new MyObject();
}
public static MyObject getInstance() {
return instance;
}
}
使用enum枚举数据类型实现单例模式
完善使用enum枚举实现单例模式
public class MyObject {
public enum MyEnumSingleton { //enum
connectionFactory;
private Connection connection;
private MyEnumSingleton() {
try {
System.out.println("创建MyObject对象");
String url = "jdbc:sqlserver://localhost:1079;databaseName=y2";
String username = "sa";
String password = "";
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(driverName);
connection = DriverManager.getConnection(url, username,
password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public Connection getConnection() {
return connection;
}
}
public static Connection getConnection() {
return MyEnumSingleton.connectionFactory.getConnection();
}
}