java多线程编程核心技术6-单例模式与多线程

前言:本章的知识非常重要,通过单例模式与多线程技术相结合,在这个过程中能发现很多以前从未考虑过的情况,一些不良的程序设计方法如果应用在商业
      项目中,将会遇到非常大的麻烦。
考虑:如何使单例模式遇到多线程是安全的、正确的。


一。立即加载/饿汉模式
1.立即加载就是使用类的时候已经将对象创建完毕,类加载的时候就已经创建了对象。
    public class MyObject {
// 立即加载方式==饿汉模式
private static MyObject myObject = new MyObject(); 
private MyObject() {

public static MyObject getInstance() {
// 此代码版本为立即加载
// 此版本代码的缺点是不能有其它实例变量
// 因为getInstance()方法没有同步
// 所以有可能出现非线程安全问题
return myObject;
}
}
    public class MyThread extends Thread { 
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());

}
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(); 
}
}
输出结果:值相同,使用的是同一个对象。


二.延迟加载/懒汉模式,调用get()方法时实例才被创建,常见的实现就是在get()方法中进行new实例化。
    public class MyObject { 
private static MyObject myObject; 
private MyObject() {

public static MyObject getInstance() {
// 延迟加载
if (myObject != null) {
} else {
   //模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
myObject = new MyObject();
}
return myObject;

}
注意,虽然判断myObject不为空才创建,但因为没有同步,多线程环境下仍有可能取出多个实例




三.使用DCL+synchronized实现单例模式:


   1.低效率的解决方案(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;

}
2.synchronized部分代码块(此种方式仍然会有线程安全问题,多线程同时进入else方法)
public class MyObject { 
private static MyObject myObject; 
private 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;

}
 
    3.使用DCL双检查锁机制(即在2的基础上,同步方法块内再次检查MyObject是否为空,这样多个线程也不会重复创建了)。
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;

}




四。使用静态内置类实现单例模式。
    public class MyObject { 
// 内部类方式
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();

private MyObject() {

public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
}
    问题:如果MyObject是可序列化对象,则通过静态内置类得到的MyObject和序列化存入文件后再取出MyObject是两个对象,可以认为读取序列化对象时系统又
     新创建了一个MyObject对象,可以使用readResolve()方法指定读取的序列化对象和静态内置类关联,保证他们只有一个实例。
 
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;

/** 要保证反序列化的MyObject的单例模式,此方法必须要有
protected Object readResolve() throws ObjectStreamException {
System.out.println("调用了readResolve方法!");
return MyObjectHandler.myObject;

*/
}
public class SaveAndRead { 
public static void main(String[] args) {
try {
MyObject myObject = MyObject.getInstance();
FileOutputStream fosRef = new FileOutputStream(new File(
"myObjectFile.txt"));
ObjectOutputStream oosRef = new ObjectOutputStream(fosRef);
oosRef.writeObject(myObject);
oosRef.close();
fosRef.close();
System.out.println(myObject.hashCode());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();

try {
FileInputStream fisRef = new FileInputStream(new File(
"myObjectFile.txt"));
ObjectInputStream iosRef = new ObjectInputStream(fisRef);
MyObject myObject = (MyObject) iosRef.readObject();
iosRef.close();
fisRef.close();
System.out.println(myObject.hashCode());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();


}

五。使用static代码块实现单例模式
    静态代码块中的代码在使用类的时候就已经执行了,所以可以应用静态代码块的这个特性来实现单例设计模式。
public class MyObject { 
private static MyObject instance = null; 
private MyObject() {

static {
instance = new MyObject();

public static MyObject getInstance() {
return instance;
}
}


六。使用枚举数据类型实现单例模式
    枚举enum和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用,也可以应用其这个特性实现单例设计模式。
public enum MyObject {
connectionFactory; 
private Connection connection; 
private MyObject() {
try {
System.out.println("调用了MyObject的构造");
String url = "jdbc:sqlserver://localhost:1079;databaseName=ghydb";
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 class MyThread extends Thread { 
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.connectionFactory.getConnection()
.hashCode());
}
}
}
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(); 
}
}

七。完善使用枚举数据类型实现单例模式
    前面将枚举类进行暴露,违反了职责单一的原则,需进行完善,更改类MyObject.java代码如下
    public class MyObject {


public enum MyEnumSingleton {
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();

}
public class MyThread extends Thread { 
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.getConnection().hashCode());
}
}
}

如上例中对枚举类的connection方法进行了包装,使调用端感觉不到枚举类的存在。












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值