当前位置: 首页 > 编程日记 > 正文

java 静态代码块 多线程,Java多线程编程笔记10:单例模式

立即加载:“饿汉模式”

立即加载就是指使用类的时候已经将对象创建完毕,常见的实现方法就是直接new实例化。也就是在调用方法前,实例就被创建了。示例代码如下所示:

class MyObject{

private static MyObject myObject=new MyObject();

private MyObject(){}

public static MyObject getInstance(){

//如果还有其他代码,存在线程安全问题

return myObject;

}

}

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();

}

}

运行结果如下:

58615885

58615885

58615885

可以发现,实现了单例模式,因为多个线程得到的实例的hashCode是一样的。

延迟加载:“懒汉模式”

延迟加载就是在调用getInstance()方法时实例才被创建,常见的方法就是在getInstance()方法中进行new实例化。实现代码如下:

class MyObject{

private static MyObject myObject;

private MyObject(){}

public static MyObject getInstance(){

if(myObject==null){ //A线程执行

myObject=new MyObject();//B线程执行

}

return myObject;

}

}

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();

}

}

但是由于在getInstance()中,存在多条语句,因此可能存在线程安全问题。运行结果也显示了这一点:

2041531420

1348345633

1348345633

甚至,当getInstance()中,有更多的语句,会出现不同的三个对象,在if(myObject==null)语句块中加入Thread.sleep(3000),运行结果如下所示:

218620763

58615885

712355351

DCL

如果使用synchronized关键字,对整个getInstance()上锁或者对整个if语句块加锁,会存在效率问题。

最终采用了DCL(Double-Check Locking)双检查锁机制,也是大多数多线程结合单例模式使用的解决方案。第一层主要是为了避免不必要的同步,第二层判断则是为了在null情况下才创建实例。

public class MyObject{

private static MyObject myObject;

private MyObject(){

}

public static MyObject getInstance(){

if (myObject == null) {//第一次检查

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (MyObject.class) {//加锁

if (myObject == null) {//第二次检查

myObject = new MyObject();//创建对象

}

}

}

return myObject;

}

}

测试结果,得到的是相同的hashcode。

上述双重检查锁定的方法从表面上看来达到了如下的效果:

多个线程试图在同一时间创建对象时,通过加锁保证只有一个线程能创建对象

在对象创建好之后,执行 getInstance() 方法将不需要获取锁,直接返回已经创建好的对象

创建对象可以分解为 分配内存;初始化;设置myObject指向的内存地址。但是由于重排序,可能导致先分配地址再进行初始化。因此,在线程A设置好了内存空间,还未初始化时,线程B判断不为空,将访问该对象,得到了一个还未初始化的对象。

解决办法有两种:

不允许初始化和设置变量指向的内存地址两步重排序(使用volatile)

允许这两步重排序,但是不允许其他线程看到这个重排序

基于volatile的解决方案

public class MyObject{

private volatile static MyObject myObject;

private MyObject(){

}

public static MyObject getInstance(){

if (myObject == null) {//第一次检查

synchronized (MyObject.class) {//加锁

if (myObject == null) {//第二次检查

myObject = new MyObject();//创建对象

}

}

}

return myObject;

}

}

由于instance是volatile修饰的,初始化和设置内存地址在多线程环境中将被禁止重排序。

基于类初始化的解决方案(静态内置类)

public class MyObject{

private static class MyObjectHandler{

private static MyObject myObject=new MyObject();

}

private MyObject(){

}

public static MyObject getInstance(){

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

return MyObjectHandler.myObject;

}

}

采用静态内置类的方法,是线程安全的。JVM在类的初始化阶段,会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁,可以同步多个线程对同一个类的初始化。虽然可能重排序,但是其他线程是无法看到这个过程的。

类的初始化过程:

通过在 Class 对象上同步(获取 Class 对象的初始化锁),控制类或接口的初始化。这个获取锁的线程会一直等待,直到当前线程能获得这个初始化锁。

线程A执行类的初始化,线程B在初始化锁对应的 condition 上等待。

线程A设置state=initialized,然后唤醒在 condition 中等待的所有线程。

其他线程获取到初始化锁,读取到state,释放该锁,然后类初始化处理过程完成。

使用static代码块

静态代码块的代码再使用类的时候就已经执行了,所以可以应用静态代码块的这个特性来实现单例设计模式。

public class MyObject{

private static MyObject myObject=null;

static{myObject=new MyObject();}

private MyObject(){

}

public static MyObject getInstance(){

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

return myObject;

}

}

使用enum枚举数据类型

使用枚举类时,和静态代码块的特性相似,构造方法会被自动调用。枚举在经过javac的编译之后,会被转换成形如public final class T extends Enum的定义。也就是说,我们定义的一个枚举,在第一次被真正用到的时候,会被虚拟机加载并初始化,而这个初始化过程是线程安全的。而我们知道,解决单例的并发问题,主要解决的就是初始化过程中的线程安全问题。

所以,由于枚举的以上特性,枚举实现的单例是天生线程安全的。同时,枚举可解决反序列化会破坏单例的问题。

enum MyObject{

INSTANCE;

}

SimpleDataFormat

SimpleDataFormat使用了单例模式,具有线程安全问题。SimpleDateFormat中的日期格式不是同步的。推荐(建议)为每个线程创建独立的格式实例。如果多个线程同时访问一个格式,则它必须保持外部同步。

解决方案1:需要的时候创建新实例

public class DateUtil {

public static String formatDate(Date date)throws ParseException{

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

return sdf.format(date);

}

public static Date parse(String strDate) throws ParseException{

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

return sdf.parse(strDate);

}

}

在需要用到SimpleDateFormat 的地方新建一个实例,不管什么时候,将有线程安全问题的对象由共享变为局部私有都能避免多线程问题,不过也加重了创建对象的负担。在一般情况下,这样其实对性能影响比不是很明显的。

解决方案2:同步SimpleDateFormat对象

public class DateSyncUtil{

private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

public static String formatDate(Date date)throws ParseException{

synchronized(sdf){

return sdf.format(date);

}

}

public static Date parse(String strDate) throws ParseException{

synchronized(sdf){

return sdf.parse(strDate);

}

}

}

当线程较多时,当一个线程调用该方法时,其他想要调用此方法的线程就要block,多线程并发量大的时候会对性能有一定的影响。

解决方案3:使用ThreadLocal

public class ConcurrentDateUtil{

private static ThreadLocal threadLocal = new ThreadLocal() {

@Override

protected DateFormat initialValue(){

return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

}

};

public static Date parse(String dateStr) throws ParseException{

return threadLocal.get().parse(dateStr);

}

public static String format(Date date){

return threadLocal.get().format(date);

}

}

使用ThreadLocal, 也是将共享变量变为独享,线程独享肯定能比方法独享在并发环境中能减少不少创建对象的开销。如果对性能要求比较高的情况下,一般推荐使用这种方法。

相关文章:

创业笔记-Node.js入门之阻塞与非阻塞

阻塞与非阻塞 正如此前所提到的,当在请求处理程序中包括非阻塞操作时就会出问题。但是,在说这之前,我们先来看看什么是阻塞操作。 我不想去解释“阻塞”和“非阻塞”的具体含义,我们直接来看,当在请求处理程序中加入阻…

vs 添加ico图 到资源

有时候想用自己做的ico 文件作为程序的图标来取代VS 程序默认的图标;在VS2005 资源视图中,打开Icon 上右击-->Add resource -->Import -->选择自己的ico 文件会跳出个错误框,说VS不支持32 位彩色图片; 网上搜索说VS不支…

HIVE QL 杂记

最近要处理用户访问日志,需要从HIVE中取数据,写了一些HIVE QL,有一点小感想,记录在此。 1. 临时表 在HIVE中进行多表连接时,可以给一些临时表命名,这样有助于理清查询语句之间的逻辑,格式为&…

java和内存交互,java内存模型-内存间交互操作

前言本文是阅读周志明大佬的《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》第12章,12.3节Java内存模型得来的读书笔记。阅读告警😂😂😂,本文可能会有点枯燥,大部分内容都是对书中内容做一…

文件读写io操作范例

系统io读写&#xff0c;copy int main(int argc, char **argv) { if(argc ! 3) { printf("Usage: %s <src> <dst>\n", argv[0]); exit(0); } int fd1, fd2; fd1 open(argv[1], O_RDONLY); fd2 open(argv[2], O_CREAT | O_TRUNC | O_WRONLY); if(…

一步步教你编写redactor的插件

Redactor是一款JQuery框架下的所见即所得在线HTML编辑器&#xff0c;具有常用的功能如图片/文件上传、表格、格式化等等&#xff0c;不仅轻量级和跨浏览器&#xff0c;还支持各种平台如PC、Mac、iPad, iPhone、Android、Refrigerators&#xff0c;更重要的它能够自动保存、自动…

windows系统杀掉explorer.exe进程后黑屏

使用“Ctrl Shift ESC”打开任务管理器 文件----> 运行新任务---->运行explorer即可 转载于:https://www.cnblogs.com/mrnx2004/p/11065573.html

php 自动返回,PHP实现自动识别Restful API的返回内容类型

如题&#xff0c;PHP如何自动识别第三方Restful API的内容&#xff0c;自动渲染成 json、xml、html、serialize、csv、php等数据&#xff1f;其实这也不难&#xff0c;因为Rest API也是基于http协议的&#xff0c;只要我们按照协议走&#xff0c;就能做到自动化识别 API 的内容…

WebKit、Gecko使用图形库

2008年11月30日 星期日 上午 01:20阅读了之后&#xff0c;觉得作为浏览器内核WebKit、Gecko&#xff0c;为了能高效美观的显示页面的内容&#xff0c;选择适当的图形库非常重要。如果图形库选择不当&#xff0c;往往会导致页面上显示的文字、图片不美观&#xff0c;看起来总让人…

office使用技巧

Word绝招:一、 输入三个“”&#xff0c;回车&#xff0c;得到一条双直线&#xff1b;二、 输入三个“~”&#xff0c;回车&#xff0c;得到一条波浪线&#xff1b;三、 输入三个“*”或 “-”或 “#”&#xff0c;回车&#xff0c;惊喜多多&#xff1b;在单元格内输入now&…

怎样用matlab打开mw文,C# matlab混合编程 MWArray使用笔记

C# matlab混合编程徐凯Email&#xff1a;xukai19871105http://www.doczj.com/doc/1a6e191fff00bed5b9f31dbf.html这几天突然想搞一搞以前没有搞定的MATLABC#混合编程&#xff0c;今天把原来编写的代码拿出来看看&#xff0c;然后结合网上一些正确的和一些错误的代码看看&#x…

【Android OpenGL ES】阅读hello-gl2代码(二)Java代码

AndroidManifest.xml <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"package"com.android.gl2jni"><applicationandroid:label"string/gl2jni_ac…

activiti任务TASK

一、概要 设计TASK的表主要是&#xff1a;ACT_RU_TASK&#xff0c;ACT_HI_TASKINST&#xff08;见参考-activiti表&#xff09;&#xff1b;任务主要有&#xff1a;人工任务&#xff08;usertask&#xff09;,服务任务&#xff08;servicetask&#xff09;等&#xff1b;候选人…

matlab数值分析拟合实例,数值分析函数拟合matlab代码.doc

数值分析函数拟合matlab代码.doc 第一题MATLAB代码用SPLINE作图XI0204060810YI098092081064038X10012Y1NEWTON3XI,YI,X源代码见M文件Y2SPLINEXI,YI,XPLOTXI,YI, O ,X,Y1, R ,X,Y2, K 用CSAPE作图XI0204060810YI098092081064038X10012Y1NEWTON3XI,YI,X源代码见M文件PPCSAPEXI,YI…

ArrayList Iterator remove java.lang.UnsupportedOperationException

在使用Arrays.asList()后调用add&#xff0c;remove这些method时出现 java.lang.UnsupportedOperationException异常。这是由于Arrays.asList() 返回java.util.Arrays$ArrayList&#xff0c; 而不是ArrayList。Arrays$ArrayList和ArrayList都是继承AbstractList&#xff0c;rem…

android中方法调用super(..)的相关知识

java中的多态有重写 方法被子类重写后 父类的原方法就会被隐藏 当你又需要调用父类所定义的原方法 这个时候就可以用super来调用super调用指向了父类&#xff0c;在一些调用里可以很巧妙的利用&#xff0c;比如监听返回键了在onKeyDown的方法里&#xff0c;如果想让系统对back…

在使用Reference Source调试.Net 源代码时如何取消optimizations(代码优化)-翻译

当你在使用Reference Source functionality in VS 2008 调试.Net 的源代码的时候&#xff0c;你会发现很多变量没法再调试时查看。 这是因为源代码服务器上提供的代码默认是为最终销售优化过的&#xff08;optimized &#xff09;。这些值虽然你没法查看&#xff0c;但不会阻断…

java旅游网站毕业论文,基于JAVA技术的旅游网站的开发.doc

摘要: 这次毕设主要是为了实现基于JAVA技术的旅游网站的开发&#xff0c;方便人们近距离的出行游玩。网站的开发过程中用到了很多方法技术&#xff0c;最主要的是JAVA技术&#xff0c;用于编写后台的功能实现代码&#xff1b;框架采用的是Spring MVC&#xff0c;作为轻量级企业…

Spring 实践 -IoC

Spring 实践标签&#xff1a; Java与设计模式 Spring简介 Spring是分层的JavaSE/EE Full-Stack轻量级开源框架.以IoC(Inverse of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程)为内核, 取代EJB的臃肿/低效/脱离现实. 主页http://spring.io/ IoC与DI IOC…

大话编程(一)

2013年1月15日 11:40:38 还有20分钟下班,实在忍不住了,想说点儿什么 编程入门的可以看看 (一)什么是0什么是1 有那么一堆叫半导体的东西,某个牛逼人用铜线连起来,组成了一个电路. 这个电路在一直通电的情况下,可以使某个点的电压保持不变, 如果这个点的电压大于某个值,就抽象为…

php 保存表单数据,使用jquery和php自动保存表单数据

我对PHP非常好,但是使用jQuery的总菜单,并且卡在自动保存表单数据中.自动保存功能在dummy.php中每30秒调用一次.我正在将用于处理的序列化表单数据( – >数据库)发送到savetest.php.此刻,我坚持这个问题&#xff1a;如何让savetest.php“监听”传入的数据并对其作出反应&…

Finalize/Dispose/Destructor

我总是会搞混这些东西&#xff0c;还是写下来帮助记忆。 Finalize 即Object.Finalize()&#xff0c;C#中不允许使用Finalize&#xff0c;析构器就等价于Finalize。 Destructor 析构器&#xff08;Destructor&#xff09;是在对象没有被引用的时候&#xff0c;由CLR自动调用的。…

linux 串口minicom配置使用

在minicom -s配置是记得取消硬件流控制。 1.minicom -o 配置文件 2.alias comminicom -o 配置文件 转载于:https://www.cnblogs.com/niceskyfly/p/5257713.html

POJ-1185 炮兵阵地 动态规划+状态压缩

由于递推的时候依赖于三个连续层的关系.一开始想着直接三重for循环,但是这里有个问题就是上一层的0位置上包括着上上层是0和1两种可能,而后者又对当前行有约束,因此该方法不行.当然有一个办法就是增加状态数,让状态能够表示是从1还是从0转移过来的.(这题有个解法是采用多进制的…

php字符串转换表达式,php处理字符串格式的计算表达式

有时候我们对每一种产品都有一个提成公式&#xff0c;而这个计算提成的公式是以字符串格式存在表中的当我们用这个计算公式时&#xff0c;他并不像我们写的&#xff1a;$a23*5;这样简单的能计算出结果&#xff0c;而它是个字符串所以&#xff0c;我们就必须把字符串转化为我们能…

JS函数式编程【译】5.2 函子 (Functors)

函子&#xff08;Functors&#xff09; 态射是类型之间的映射&#xff1b;函子是范畴之间的映射。可以认为函子是这样一个函数&#xff0c;它从一个容器中取出值&#xff0c; 并将其加工&#xff0c;然后放到一个新的容器中。这个函数的第一个输入的参数是类型的态射&#xff0…

[转]Introduction of iSCSI Target in Windows Server 2012

Introduction of iSCSI Target in Windows Server 2012 源地址&#xff1a;http://blogs.technet.com/b/filecab/archive/2012/05/21/introduction-of-iscsi-target-in-windows-server-2012.aspx The iSCSI Target made its debut as a free download for Windows 2008 R2 in A…

全国移动联通基站数据升级包(2013年1月基站升级包).rar

“全国移动联通基站数据升级包(2013年1月基站升级包).rar” 已经上传到CNBLOGS 地址&#xff1a;http://files.cnblogs.com/topwang-com/%E5%85%A8%E5%9B%BD%E7%A7%BB%E5%8A%A8%E8%81%94%E9%80%9A%E5%9F%BA%E7%AB%99%E6%95%B0%E6%8D%AE%E5%8D%87%E7%BA%A7%E5%8C%85(2013%E5%B9%…

php自动计算增长率,如何写sql计算增长率?

问题已有数据表(假定表名为t)time sale1999 4844904672000 651413668.92001 13713710082002 18177416252003 25053320952004 37654384862005 48177203842006 6083322598需要产生如下的数据表time sale …

我先了解一下博客园创建随笔/文章/日记的过程与三者的区别(隐私等级,是否审核等)...

我先了解一下博客园创建随笔/文章/日记的过程与三者的区别(隐私等级,是否审核等)转载于:https://www.cnblogs.com/Totooria-Hyperion/p/5260289.html