一、单例模式
1.1 立即加载/饿汉模式
立即加载就是使用类的时候已经创建完毕
立即加载:
public class single {
static User user = new User();
public static User getInstance(){
return user;
}
}
测试:
@Override
public void run() {
System.out.println(single.getInstance().hashCode());
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new MyThread().start();
}
}
}
结果:
610025186
610025186
610025186
1.2 延迟加载/懒汉模式
延迟加载就是调用ge方法时才开始创建对象
- 同步方法保证线程安全,但是效率很低
public class single {
static User user;
public synchronized static User getInstance(){
try {
if(user == null){
Thread.sleep(2000);
user = new User();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return user;
}
}
- 同步代码块保证线程安全,但是效率也低,注意要使用类锁
public class single {
static User user;
public static User getInstance() {
try {
synchronized (single.class) {
if (user == null) {
Thread.sleep(2000);
user = new User();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return user;
}
}
- 针对某些重要的代码进行单独同步
public class single {
static User user;
public static User getInstance() {
try {
if (user != null) {
} else {
Thread.sleep(2000);
synchronized (single.class) {
user = new User();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return user;
}
}
结果:效率提升了,但是多线程下还是不安全
553602986
1354708602
964250181
- 使用DCL双锁检查机制
public class single {
static User user;
public static User getInstance() {
try {
if (user != null) {
} else {
Thread.sleep(2000);
synchronized (single.class) {
if(user == null)
user = new User();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return user;
}
}
作用:保证了不需要同步执行代码的异步执行性,又保证了单利的效果。
二、其他方法实现单例模式
2.1 静态内置类
public class single {
public static class InSingle{
static User user = new User();
public static User getInstance(){
return user;
}
}
}
和普通的饿汉模式差不多,只是多了一层包裹
2.2 序列化与反序列化
静态内置类如果遇到序列化对象时,还是会出现线程安全的问题
过当序列化遇到单例时,里边就有了个问题:从内存读出而组装的对象破坏了单例的规则。单例是要求一个JVM中只有一个类对象的,而现在通过反序列,一个新的对象克隆了出来。
我们必须得使用readResolve()方法,这样,当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象了,单例规则也就得到了保证。
- 序列化内置类
public class Single implements Serializable {
public static final Long serialVersionUID = 122L;
public static class InSingle {
public static final Single single = new Single();
}
private Single(){
}
public static Single getInstance(){
return InSingle.single;
}
}
- 反序列化
public class test {
public static void main(String[] args) {
try {
User instance = single.InSingle.getInstance();
FileOutputStream fileOutputStream = new FileOutputStream(new File("instance.txt"));
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(instance);
objectOutputStream.close();
fileOutputStream.close();
System.out.println(instance.hashCode());
FileInputStream fileInputStream = new FileInputStream(new File("instance.txt"));
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
User object = (User) inputStream.readObject();
inputStream.close();
fileInputStream.close();
System.out.println(object.hashCode());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出:
1595428806
1469821799
对象在序列化之前和序列化后从文件读出已经不是同一个对象了。借助OblectInputStream和ObjectOutputStream,存储和读取参与序列化的类。
- 解决方法
public class Single implements Serializable {
public static final Long serialVersionUID = 122L;
public static class InSingle {
public static final Single single = new Single();
}
private Single(){
}
public static Single getInstance(){
return InSingle.single;
}
public Object readResolve() throws ObjectStreamException{
System.out.println("调用了一个奇怪的方法");
return InSingle.single;
}
}
原理:我们必须得使用readResolve()方法,这样,当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象了,单例规则也就得到了保证。
- 最好是实现自己的单利模式,尽量别实现其他类的单例模式,可能会出现意想不到的错误。
2.3 使用static代码块
public class Single {
public static Single single = null;
static {
single = new Single();
}
public static Single getInstance(){
return single;
}
}
2.4 使用枚举数据类型
- 枚举类型和静态代码块的特征相似,在使用枚举类时,构造方法会被自动调用,也可以应用这个特性实现单例模式。
- 枚举中的一个元素的构造方法只会调用一次
- 枚举中的元素是枚举类型的。
内置枚举类:
public class SingGood {
public static enum Goal{
Interface;
private User user;
private Goal(){
user = new User();
}
public User getInstance(){
return user;
}
}
public static User getInstance(){
return Goal.Interface.getInstance();
}
}
测试:
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(SingGood.getInstance().hashCode());
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new MyThread().start();
}
}
}
结果:
973798264
973798264
973798264