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方法进行了包装,使调用端感觉不到枚举类的存在。












代码转载自:https://pan.quark.cn/s/8ce4326d996e 对于在 CentOS 7 系统中修改网卡配置文件后无法使设置生效的情况,经过实践验证,可以通过使用 nmcli 命令来进行调整。完成修改之后,需要重新启动虚拟机以使更改生效,这样操作流程即告完成。如果设置仍然无法生效,则表明虚拟机在启动过程中所获取的 IP 地址配置并非针对 eth0,此时可以对其它网卡的配置文件进行修改或将其移除。在 CentOS 7 系统中,网络配置的管理机制早期版本存在差异,主要体现为采用了 Network Manager 服务来负责网络接口的管理。在某些情形下,尽管修改了 `/etc/sysconfig/network-scripts` 目录下的 `ifcfg-eth0` 文件,但网络配置却未能即时生效。此类问题的发生通常源于 CentOS 7 采用了不同于以往的配置读取方法。接下来将具体阐述如何借助 nmcli 命令来处理这一挑战。 以 root 用户身份登录系统并打开终端界面。nmcli 是 Network Manager 提供的命令行界面工具,它支持在命令行环境下执行网络连接的建立、编辑、查询及管理任务。针对修改 eth0 网卡配置的需求,可以遵循以下步骤进行操作: 1. 导航至 `/etc/sysconfig/network-scripts` 目录: ``` cd /etc/sysconfig/network-scripts ``` 2. 检查该目录内是否存在 `ifcfg-eth0.bak` 文件,该备份文件可能是先前调整配置时遗留下来的,若存在可能造成冲突。若发现该文件,可以选择将其删除: ``` [root@localhost netw...
代码转载自:https://pan.quark.cn/s/46fd08fb879c 网管教程 从入门到精通软件篇 ★一。★详尽的xp修复控制台指令及其应用!!! 放入xp(2000)的光盘,安装时选择R,执行修复! Windows XP(涵盖 Windows 2000)的控制台指令是在系统遭遇某些意外状况时的一种极具效用的诊断、检测以及恢复系统功能的工具。笔者确实一直期望能够将这方面的指令进行归纳,此次由老范辛苦整理了这份极具价值的秘籍。 Bootcfg bootcfg 命令用于启动配置故障恢复(对大多数计算机而言,即 boot.ini 文件)。 带有特定参数的 bootcfg 命令仅在运用故障恢复控制台时方可使用。能够在命令行界面下运用带有不同参数的 bootcfg 命令。 用法: bootcfg /default 设定默认引导选项。 bootcfg /add 向引导清中增添 Windows 安装。 bootcfg /rebuild 重复整个 Windows 安装流程并让用户选择需添加的项目。 注意:运用 bootcfg /rebuild 之前,应先借助 bootcfg /copy 命令备份 boot.ini 文件。 bootcfg /scan 探查用于 Windows 安装的全部磁盘并展示结果。 注意:这些结果被静态存储,并用于当前会话。若在当前会话期间磁盘配置发生变动,为获取更新的探查结果,必须先重启计算机,然后再次探查磁盘。 bootcfg /list 列示引导清中已有的项目。 bootcfg /disableredirect 在启动引导程序中禁用重定向。 bootcfg /redirect [ PortBaudRrate] |[ useBio...
代码下载链接: https://pan.quark.cn/s/fc524f791b68 AA制程,即Active Alignment,被理解为主动对准,是一种用于确定零部件装配中相对位置的方法。在摄像头封装阶段,涉及图像传感器、镜座、马达、镜头、线路板等多个部件的重复组装,而传统的封装设备如CSP及COB等,均是依据设备设定的参数进行零部件的移动装配,因而零部件的叠加误差会逐渐增大,最终在摄像头上表现为拍照最清晰的位置可能偏离画面中心、四边清晰度不均等现象。伴随智能手机和其他高端电子产品的普及,摄像头模组的性能正日益受到重视。高分辨率、卓越的低光表现以及稳定视频输出是现代用户所期望的。在摄像头模组的制造环节,各部件的精准定位对成像质量具有决定性作用。因此,一种名为“AA制程”(Active Alignment)的前沿技术被开发出来,成为摄像头精密对准的核心技术。 AA制程,即Active Alignment,是一种在摄像头封装过程中应用的主动对准方法。该方法在多个组件装配阶段发挥作用,涵盖图像传感器、镜座、马达、镜头和线路板等部件。传统的封装方式,如CSP(Chip Scale Package)和COB(Chip On Board),依赖于设备预设的参数进行组装,但随着组件数量的增加,误差也会累积,最终影响摄像头的表现。如在成像质量上可能出现中心位置偏移、四角清晰度不一致等问题。 AA制程技术的核心在于实时监测主动调整。在组装过程中,它借助先进的检测设备持续监控半成品的状态,并根据实时信息对组装部件进行精确修正,从而显著降低装配误差。通过这种技术,能够确保摄像头模组中各组件的相对位置准确无误,从而使得最终的成像效果更加稳定,特别是在中心区域和四角的清晰度上...
内容概要:本文介绍了一套基于Matlab实现的光子晶体90度弯曲波导的二维时域有限差分法(2D FDTD)仿真代码,旨在通过数值模拟手段深入研究光子晶体波导中的光传播特性。该资源聚焦于电磁场光子学领域的仿真技术应用,系统实现了FDTD算法在复杂介质结构中的建模过程,涵盖空间网格剖分、时间步进迭代、完美匹配层(UPML)边界条件处理、总场散射场(TFSF)激励源设置、介电常数分布定义及电磁场演化可视化等核心模块,能够有效分析光在90度弯曲波导中的传输效率、模式分布反射损耗等关键性能指标。; 适合人群:具备电磁场理论基础和Matlab编程能力的研究生、科研人员以及从事光子晶体器件设计仿真的工程技术人员。; 使用场景及目标:①用于教学演示FDTD方法的基本原理算法流程,帮助理解麦克斯韦方程的离散化求解过程;②支撑科研工作中对光子晶体弯曲波导结构的传输特性进行仿真分析性能优化;③作为开发更复杂光子集成器件(如分束器、滤波器)数值仿真工具的基础框架; 阅读建议:建议使用者结合经典FDTD教材(如Taflove著作)深入理解算法理论,并在Matlab环境中逐模块调试代码,重点关注电场磁场的交替更新过程、UPML吸收边界的设计实现以及TFSF源的引入方式,从而全面提升对时域电磁仿真机制的掌握应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值