您的位置:首页 > 编程语言 > Java开发

Java单例模式与多线程

2016-07-24 12:50 465 查看
该篇博客,将会通过单例模式和多线程技术相结合的方式来学习,在此期间,我们会遇到之前没有考虑过的情况,那么,废话不多说,先来看看,实现单例模式的两种方法

饿汉模式

public class MyObjectHungery {

private static MyObjectHungery sMyObject = new MyObjectHungery();

private MyObjectHungery() {

}
public static MyObjectHungery getMyObjectInstance() {
return sMyObject;
}
}


懒汉模式

public class MyObjectLazy {

private static MyObjectLazy sMyObject = null;

private MyObjectLazy() {

}
public static MyObjectLazy getMyObjectInstance() {
if (sMyObject == null) {
sMyObject = new MyObjectLazy();
}
return sMyObject;
}
}


懒汉和饿汉模式的缺点

上面虽然使用”立即加载”和”延迟加载”实现了单例设计模式,但是在多线程下,根本不能实现 保持単例的状态。看下面的代码:

public class MyObjectLazy {

private static MyObjectLazy sMyObject = null;

private MyObjectLazy() {

}
public static MyObjectLazy getMyObjectInstance() {
try {
if (sMyObject == null) {
// 懒汉模式,模拟创建对象时候的一些耗时操作
Thread.sleep(3000);
sMyObject = new MyObjectLazy();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return sMyObject;
}

private static class MyThread extends Thread{

@Override
public void run() {
super.run();
// 通过懒汉模式获得当前単例对象,并打印其hashCode
System.out.println("current object is : "+MyObjectLazy.getMyObjectInstance().hashCode());
}
}

public static void main(String[] args) {
// 模拟多线程环境下,通过打印当前对象的hashCode,判断懒汉模式存在的多线程问题
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}


此时打印结果如下:



懒汉模式的解决方案

声明synchronized关键字

public synchronized static MyObjectLazy getMyObjectInstance() {
try {
if (sMyObject == null) {
// 懒汉模式,模拟创建对象时候的一些耗时操作
Thread.sleep(3000);
sMyObject = new MyObjectLazy();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return sMyObject;
}


此时程序运行结果:



可以看到我们在此方法上加入了synchronized关键字得到相同实例对象,但是这种方法的运行效率非常低下,是同步运行的,下一个线程想要取得对象,就必须要等到上一个线程释放锁之后,才可以。

同步代码块来解决

同步代码块可以针对某些重要的代码进行单独同步,而其他代码不需要同步,这样可以大幅提升效率。

public static MyObjectLazy getMyObjectInstance() {
try {
if (sMyObject == null) {
// 懒汉模式,模拟创建对象时候的一些耗时操作
Thread.sleep(3000);
synchronized (MyObjectLazy.class) {
if (sMyObject == null) {
sMyObject = new MyObjectLazy();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return sMyObject;
}




使用静态内部类实现单例模式

public class MyObjectStatic {

private static class GetStaticObj {
private static MyObjectStatic sMyObjectStatic = new MyObjectStatic();
}
private MyObjectStatic() {

}
public static MyObjectStatic getMyObjectInstance() {
return GetStaticObj.sMyObjectStatic;
}
}


序列化与反序列化的単例模式

上面的代码,使用静态内部类虽然可以达到线程安全,但是如果遇到序列化和反序列化对象时候,还是多例的。看下面代码:

public class MyObjectStatic implements Serializable{

private static final long serialVersionUID = -4956049192964237265L;

private static class GetStaticObj {
private static MyObjectStatic sMyObjectStatic = new MyObjectStatic();
}
private MyObjectStatic() {

}
public static MyObjectStatic getMyObjectInstance() {
return GetStaticObj.sMyObjectStatic;
}

private static class MyThread extends Thread{

@Override
public void run() {
super.run();
// 通过懒汉模式获得当前単例对象,并打印其hashCode
System.out.println("current object is : "+MyObjectStatic.getMyObjectInstance().hashCode());
}
}

public static void main(String[] args) {
// 将获得的単例模式序列化对象写入到文件里,并打印其hashcode
try {
MyObjectStatic myObjectStatic = MyObjectStatic.getMyObjectInstance();
OutputStream os = new FileOutputStream(new File("D:/object.txt"));
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(myObjectStatic);
os.close();
oos.close();
System.out.println("myObjectStatic write is :"+myObjectStatic.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
// 从文件里读取写入的学序列化単例对象,并打印其hashcode
try {
InputStream is = new FileInputStream(new File("D:/object.txt"));
ObjectInputStream ois = new ObjectInputStream(is);
MyObjectStatic myObjectStatic = (MyObjectStatic) ois.readObject();
is.close();
ois.close();
System.out.println("myObjectStatic read is :"+myObjectStatic.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}




可以看到,这里我们写入和读取的是不同的对象。

这里,我们在単例类中加入下面代码:

// 在反序列化中使用readResolve方法
private Object readResolve() {
System.out.println("readResolve method runs ......");
return GetStaticObj.sMyObjectStatic;
}


此时效果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: