单例模式与多线程
Posted Java与大数据学习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式与多线程相关的知识,希望对你有一定的参考价值。
参考书籍《Java多线程编程核心技术》
通过单例模式与多线程技术相结合,在这个过程中发现很多以前从未考虑过的情况,一些不良的程序设计方法如果应用到商业项目中,将会遇到非常大的麻烦。所以要考虑一件事,如何使单例模式遇到多线程是安全的,正确的。
立即加载/“饿汉模式”
立即加载就是使用类的时候已经将对象创建完毕,常见的实现方法就是直接new实例化。
class MyObject{
//立即加载
private static MyObject myObject = new MyObject();
private MyObject(){}
public static MyObject getInstance(){
//缺点是不能有其他实例变量
//因为getInstance()方法没有同步
//所以可能出现非线程安全问题
return myObject;
}
}
延迟加载/“懒汉模式”
延迟加载就是在调用get()方法时实例才被创建,常见的实现方法是在get()方法中进行new实例化。
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;
}
}
使用DCL双检查锁机制
DCL是大多数多线程结合单例模式使用的解决方案
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;
}
}
使用静态内部类
class MyObject{
//内部类方式
private static class MyObjectHandler{
private static MyObject myObject = new MyObject();
}
private MyObject(){}
public static MyObject getInstance(){
return MyObjectHandler.myObject;
}
}
序列化与反序列化的单例模式实现
静态内部类可以达到线程安全问题,但如果遇到序列化对象时,得到的结果还是多例的。
测试代码:
package com.sixj.Thread;
import java.io.*;
/*
Created by sixj 2019/3/12 20:45
*/
public class SaveAndRead {
public static void main(String[] args) {
try {
MyObject myObject = MyObject.getInstance();
FileOutputStream fos = new FileOutputStream(new File("myObjectFile.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myObject);
oos.close();
fos.close();
System.out.println(myObject.hashCode());
} catch (IOException e) {
e.printStackTrace();
}
try {
FileInputStream fis = new FileInputStream(new File("myObjectFile.txt"));
ObjectInputStream ois = new ObjectInputStream(fis);
MyObject myObject = (MyObject)ois.readObject();
ois.close();
fis.close();
System.out.println(myObject.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
所以要做一下修改,在静态内部类的基础上加一个readResolve()方法
class MyObject implements Serializable {
//内部类方式
private static class MyObjectHandler{
private static 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代码块实现
静态代码块中的代码在使用类的时候就已经执行了,所以可以应用静态代码块的这个特性来实现单例模式。
class MyObject{
private static MyObject instance = null;
private MyObject(){}
static {
instance = new MyObject();
}
public static MyObject getInstance(){
return instance;
}
}
使用enum枚举数据类型实现
枚举enum和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用,也可以应用其这个实现单例模式。
通过enum枚举获得单例的Connection对象:
class Connection {}
class MyObject{
public enum MyEnumSingleton{
connectionFactory;
private Connection connection;
private MyEnumSingleton(){
connection = new Connection();
}
public Connection getConnection(){
return connection;
}
}
public static Connection getConnection(){
return MyEnumSingleton.connectionFactory.getConnection();
}
}
测试:
class S_Thread extends Thread{
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println(MyObject.getConnection().hashCode());
}
}
}
public class TestSingleton {
public static void main(String[] args) {
S_Thread s1 = new S_Thread();
S_Thread s2 = new S_Thread();
S_Thread s3 = new S_Thread();
s1.start();
s2.start();
s3.start();
}
}
运行结果:
以上是关于单例模式与多线程的主要内容,如果未能解决你的问题,请参考以下文章