
多线程与单例模式
发布日期:2021-05-10 07:25:37
浏览次数:12
分类:精选文章
本文共 10136 字,大约阅读时间需要 33 分钟。
立即加载/饿汉模式
立即加载是指在使用类的时候对象已经创建完毕,常见的实现方法是直接new实例化。在立即加载/饿汉模式中,调用方法前,实例已经被创建了,下面来看一下实现代码:
package com.single.thread;public class SingleInstance { private static SingleInstance instance=new SingleInstance(); private SingleInstance(){ } public static SingleInstance getInstance(){ return instance; }}class test{ public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance.getInstance().hashCode()); } }); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); Thread thread3 = new Thread(thread); thread1.start(); thread2.start(); thread3.start(); }}测试结果:208462095520846209552084620955
控制台输出的结果看出hashcode是一个值完成了立即加载的单例模式。但是缺点是不能拥有其他的实例变量,因为getinstance方法不是同步的,所以可能出现非线程安全问题。
延迟加载/懒汉模式
延迟加载是指调用方法的时候对象才实例化。
package com.single.thread;public class SingleInstance2 { private static SingleInstance2 instance; private SingleInstance2(){ } public static SingleInstance2 getInstance(){ if(instance==null){ instance=new SingleInstance2(); } return instance; }}class test2{ public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance2.getInstance().hashCode()); } }); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); Thread thread3 = new Thread(thread); thread1.start(); thread2.start(); thread3.start(); }}结果:3436921117124476241300234150
控制台输出的hashcode不同说明创建了三个对象并不是单例的。
懒汉模式的解决方案
只需要在getinstance方法上加上同步关键字synchronizedpackage com.single.thread;public class SingleInstance2 { private static SingleInstance2 instance; private SingleInstance2(){ } public synchronized static SingleInstance2 getInstance(){ if(instance==null){ instance=new SingleInstance2(); } return instance; }}class test2{ public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance2.getInstance().hashCode()); } }); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); Thread thread3 = new Thread(thread); thread1.start(); thread2.start(); thread3.start(); }}结果:174658297717465829771746582977
此方法加上synchronized关键字得到了相同的实例对象,但是这种方法效率低,是同步运行的。下一个线程必须等上一个线程释放锁才能进入此方法。
能否使用同步代码块来解决下面来看一个案例package com.single.thread;public class SingleInstance3 { private static SingleInstance3 instance; private SingleInstance3(){ } public static SingleInstance3 getInstance(){ try { if(instance==null){ Thread.sleep(200); synchronized (SingleInstance3.class) { instance = new SingleInstance3(); } } }catch (Exception e){ e.printStackTrace(); } return instance; }}class test3{ public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance3.getInstance().hashCode()); } }); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); Thread thread3 = new Thread(thread); thread1.start(); thread2.start(); thread3.start(); }}结果:18772129148470862621688623490
此方法使用同步synchronized语句块对实例化对象的关键代码进行同步,运行效率得到了提升,但是从结果上来看并没有解决多线程问题。下面使用
双检查锁机制来解决多线程下加载饿汉单例模式。package com.single.thread;public class SingleInstance4 { private volatile static SingleInstance4 instance; private SingleInstance4(){ } public static SingleInstance4 getInstance(){ try { if(instance==null){ Thread.sleep(200); synchronized (SingleInstance4.class) { if(instance==null) instance = new SingleInstance4(); } } }catch (Exception e){ e.printStackTrace(); } return instance; }}class test4{ public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance4.getInstance().hashCode()); } }); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); Thread thread3 = new Thread(thread); thread1.start(); thread2.start(); thread3.start(); }}结果:168862349016886234901688623490
使用volatile关键字可以使instance变量在多线程间达到可见性,另外也会禁止指令重排,因为 instance = new SingleInstance4()代码在内存部分为三个部分
1)memory=allocate(); //分配对象的内存空间 2) ctorInstance(memory); //初始化对象 3)instance=memory; //设置instance指向刚分配的内存地址JIT编译器有可能将这三个步骤重排序成:
1)memory=allocate(); //分配对象的内存空间
2) instance=memory; //设置instance指向刚分配的内存地址 3)ctorInstance(memory); //初始化对象 这时候将会出现以下情况:虽然构造方法还没有执行,但是instance对象有了内存地址,值不是null,当访问instance对象中的实量还是默认数据类型的默认值。使用静态内部类实现单例模式
public class SingleInstance5 { private SingleInstance5(){ } private static class InnerInstance{ private static SingleInstance5 instance=new SingleInstance5(); } public static SingleInstance5 getInstance(){ return InnerInstance.instance; }}class test5{ public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance5.getInstance().hashCode()); } }); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); Thread thread3 = new Thread(thread); thread1.start(); thread2.start(); thread3.start(); }}结果:389107613891076138910761
从控制台输出的结果来看可以知道静态内部类可以实现单例模式。
序列化与反序列化的单列模式实现
public class User { }package com.single.xue;import java.io.ObjectStreamException;import java.io.Serializable;public class MyObject implements Serializable { private static final long serialVersionUID=666L; public static User user=new User(); private static MyObject myObject=new MyObject(); private MyObject(){ } public static MyObject getInstance(){ return myObject; } //该方法在反序列化时不创建新的instance对象 protected Object readResolve() throws ObjectStreamException { System.out.println("该方法被调用了"); return MyObject.myObject; }}
测试类:
import java.io.*;public class Test { public static void main(String[] args) { MyObject instance = MyObject.getInstance(); FileOutputStream fileOutputStream=null; ObjectOutputStream objectOutputStream=null; FileInputStream fileInputStream=null; ObjectInputStream objectInputStream=null; try { System.out.println("序列化——instance=" + instance.hashCode() + "\t" + "user=" + MyObject.user.hashCode()); fileOutputStream = new FileOutputStream(new File("e:\\myObject.txt")); objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(instance); fileOutputStream.close(); objectOutputStream.close(); }catch (Exception e){ e.printStackTrace(); } try{ fileInputStream=new FileInputStream(new File("e:\\myObject.txt")); objectInputStream=new ObjectInputStream(fileInputStream); MyObject object = (MyObject)objectInputStream.readObject(); fileInputStream.close(); objectInputStream.close(); System.out.println("序列化——instance=" + instance.hashCode() + "\t" + "user=" + MyObject.user.hashCode()); }catch (Exception e){ e.printStackTrace(); } }}结果:序列化——instance=557041912 user=1134712904该方法被调用了序列化——instance=557041912 user=1134712904
静态代码块实现单例模式
package com.single.thread;public class SingleInstance6 { private static SingleInstance6 instance=null; static { instance=new SingleInstance6(); } private SingleInstance6(){ } public static SingleInstance6 getInstance(){ return instance; }}class test6{ public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { System.out.println(SingleInstance6.getInstance().hashCode()); } }); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); Thread thread3 = new Thread(thread); thread1.start(); thread2.start(); thread3.start(); }}结果:566612817566612817566612817
静态代码块在使用类的时候就已经执行,所有可以应用静态代码块的这个特性来实现单例。
使用enum枚举数据类型来实现单例模式
public class MyObject { public enum myEnumSingleton{ users; private User user; private myEnumSingleton() { user=new User(); } public User getUser(){ return user; } } public static User getUser(){ return myEnumSingleton.users.getUser(); }}class User{ }class Tests{ public static void main(String[] args) { Thread thread=new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 2; i++) { System.out.println(MyObject.getUser().hashCode()); } } }); Thread thread1 = new Thread(thread); Thread thread2 = new Thread(thread); Thread thread3 = new Thread(thread); thread1.start(); thread2.start(); thread3.start(); }}结果:370102716370102716370102716370102716370102716370102716
enum枚举类型的特性和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用,所有可以用来实现单例模式。
发表评论
最新留言
不错!
[***.144.177.141]2025年04月23日 17时00分57秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
周报十一
2021-05-10
git拉取远程指定分支代码
2021-05-10
C语言--C语言总结大纲
2021-05-10
轻松理解前后端分离(通俗易懂)
2021-05-10
JavaFX官方文档
2021-05-10
ORA-12154: TNS: 无法解析指定的连接标识符
2021-05-10
Spring学习总结(十二):Spring中的事务管理
2021-05-10
G7相关
2021-05-10
Linux 激活网卡ifconfig eth1 up 和 ifup eth1 之间的差别
2021-05-10
In App Purchase Verification using PHP
2021-05-10
Mapper.xml中新增数据并返回主键ID(MYSQL)
2021-05-10
shell编程===》进程锁
2021-05-10
教你如何一招破解简单的joy.dex的卡密系统(Auto.js)
2021-05-10
Linux小操作LVM
2021-05-10
Split返回数组元素含有空字符串移除
2021-05-10
解决VS中C语言运行scanf错误
2021-05-10
Java注解
2021-05-10
idea上的程序报错-> 错误: 找不到或无法加载主类
2021-05-10
SQLServer 安装提示需要重启计算机的解决方案
2021-05-10