java单例方法_Java单例模式

单例模式,是特别常见的一种设计模式,因此我们有必要对它的概念和几种常见的写法非常了解,而且这也是面试中常问的知识点。

所谓单例模式,就是所有的请求都用一个对象来处理,如我们常用的Spring默认就是单例的,而多例模式是每一次请求都创建一个新的对象来处理,如structs2中的action。

使用单例模式,可以确保一个类只有一个实例,并且易于外部访问,还可以节省系统资源。如果在系统中,希望某个类的对象只存在一个,就可以使用单例模式。

那怎么确保一个类只有一个实例呢?

我们知道,通常我们会通过new关键字来创建一个新的对象。这个时候类的构造函数是public公有的,你可以随意创建多个类的实例。所以,首先我们需要把构造函数改为private私有的,这样就不能随意new对象了,也就控制了多个实例的随意创建。

然后,定义一个私有的静态属性,来代表类的实例,它只能类内部访问,不允许外部直接访问。

最后,通过一个静态的公有方法,把这个私有静态属性返回出去,这就为系统创建了一个全局唯一的访问点。

以上,就是单例模式的三个要素。总结为:私有构造方法

指向自己实例的私有静态变量

对外的静态公共访问方法

单例模式分为饿汉式和懒汉式。它们的主要区别就是,实例化对象的时机不同。饿汉式,是在类加载时就会实例化一个对象。懒汉式,则是在真正使用的时候才会实例化对象。

饿汉式单例代码实现:

public class Singleton {

// 饿汉式单例,直接创建一个私有的静态实例

private static Singleton singleton = new Singleton();

//私有构造方法

private Singleton(){

}

//提供一个对外的静态公有方法

public static Singleton getInstance(){

return singleton;

}

}

懒汉式单例代码实现

public class Singleton {

// 懒汉式单例,类加载时先不创建实例

private static Singleton singleton = null;

//私有构造方法

private Singleton(){

}

//真正使用时才创建类的实例

public static Singleton getInstance(){

if(singleton == null){

singleton = new Singleton();

}

return singleton;

}

}

稍有经验的程序员就发现了,以上懒汉式单例的实现方式,在单线程下是没有问题的。但是,如果在多线程中使用,就会发现它们返回的实例有可能不是同一个。我们可以通过代码来验证一下。创建十个线程,分别启动,线程内去获得类的实例,把实例的 hashcode 打印出来,只要相同则认为是同一个实例;若不同,则说明创建了多个实例。

public class TestSingleton {

public static void main(String[] args) {

for (int i = 0; i < 10 ; i++) {

new MyThread().start();

}

}

}

class MyThread extends Thread {

@Override

public void run() {

Singleton singleton = Singleton.getInstance();

System.out.println(singleton.hashCode());

}

}

/**

运行多次,就会发现,hashcode会出现不同值

668770925

668770925

649030577

668770925

668770925

668770925

668770925

668770925

668770925

668770925

*/

所以,以上懒汉式的实现方式是线程不安全的。那饿汉式呢?你可以手动测试一下,会发现不管运行多少次,返回的hashcode都是相同的。因此,认为饿汉式单例是线程安全的。

那为什么饿汉式就是线程安全的呢?这是因为,饿汉式单例在类加载时,就创建了类的实例,也就是说在线程去访问单例对象之前就已经创建好实例了。而一个类在整个生命周期中只会被加载一次。因此,也就可以保证实例只有一个。所以说,饿汉式单例天生就是线程安全的。(可以了解一下类加载机制)

既然懒汉式单例不是线程安全的,那么我们就需要去改造一下,让它在多线程环境下也能正常工作。以下介绍几种常见的写法:

1) 使用synchronized方法

实现非常简单,只需要在方法上加一个synchronized关键字即可

public class Singleton {

private static Singleton singleton = null;

private Singleton(){

}

//使用synchronized修饰方法,即可保证线程安全

public static synchronized Singleton getInstance(){

if(singleton == null){

singleton = new Singleton();

}

return singleton;

}

}

这种方式,虽然可以保证线程安全,但是同步方法的作用域太大,锁的粒度比较粗,因此,执行效率就比较低。

2) synchronized 同步块

既然,同步整个方法的作用域大,那我缩小范围,在方法里边,只同步创建实例的那一小部分代码块不就可以了吗(因为方法较简单,所以锁代码块和锁方法没什么明显区别)。

public class Singleton {

private static Singleton singleton = null;

private Singleton(){

}

public static Singleton getInstance(){

//synchronized只修饰方法内部的部分代码块

synchronized (Singleton.class){

if(singleton == null){

singleton = new Singleton();

}

}

return singleton;

}

}

这种方法,本质上和第一种没什么区别,因此,效率提升不大,可以忽略不计。

3) 双重检测(double check)

可以看到,以上的第二种方法只要调用getInstance方法,就会走到同步代码块里。因此,会对效率产生影响。其实,我们完全可以先判断实例是否已经存在。若已经存在,则说明已经创建好实例了,也就不需要走同步代码块了;若不存在即为空,才进入同步代码块,这样可以提高执行效率。因此,就有以下双重检测了:

public class Singleton {

//注意,此变量需要用volatile修饰以防止指令重排序

private static volatile Singleton singleton = null;

private Singleton(){

}

public static Singleton getInstance(){

//进入方法内,先判断实例是否为空,以确定是否需要进入同步代码块

if(singleton == null){

synchronized (Singleton.class){

//进入同步代码块时也需要判断实例是否为空

if(singleton == null){

singleton = new Singleton();

}

}

}

return singleton;

}

}

需要注意的一点是,此方式中,静态实例变量需要用volatile修饰。因为,new Singleton() 是一个非原子性操作,其流程为:

a.给 singleton 实例分配内存空间

b.调用Singleton类的构造函数创建实例

c.将 singleton 实例指向分配的内存空间,这时认为singleton实例不为空

正常顺序为 a->b->c,但是,jvm为了优化编译程序,有时候会进行指令重排序。就会出现执行顺序为 a->c->b。这在多线程中就会表现为,线程1执行了new对象操作,然后发生了指令重排序,会导致singleton实例已经指向了分配的内存空间(c),但是实际上,实例还没创建完成呢(b)。

这个时候,线程2就会认为实例不为空,判断 if(singleton == null)为false,于是不走同步代码块,直接返回singleton实例(此时拿到的是未实例化的对象),因此,就会导致线程2的对象不可用而使用时报错。

4)使用静态内部类

思考一下,由于类加载是按需加载,并且只加载一次,所以能保证线程安全,这也是为什么说饿汉式单例是天生线程安全的。同样的道理,我们是不是也可以通过定义一个静态内部类来保证类属性只被加载一次呢。

public class Singleton {

private Singleton(){

}

//静态内部类

private static class Holder {

private static Singleton singleton = new Singleton();

}

public static Singleton getInstance(){

//调用内部类的属性,获取单例对象

return Holder.singleton;

}

}

而且,JVM在加载外部类的时候,不会加载静态内部类,只有在内部类的方法或属性(此处即指singleton实例)被调用时才会加载,因此不会造成空间的浪费。

5)使用枚举类

因为枚举类是线程安全的,并且只会加载一次,所以利用这个特性,可以通过枚举类来实现单例。

public class Singleton {

private Singleton(){

}

//定义一个枚举类

private enum SingletonEnum {

//创建一个枚举实例

INSTANCE;

private Singleton singleton;

//在枚举类的构造方法内实例化单例类

SingletonEnum(){

singleton = new Singleton();

}

private Singleton getInstance(){

return singleton;

}

}

public static Singleton getInstance(){

//获取singleton实例

return SingletonEnum.INSTANCE.getInstance();

}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/497961.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

process java类_Java中Process类的用途是什么?

Java.lang.Process是Object类的子类&#xff0c;可以描述由Runtime类的exec()方法启动的进程。 进程和对象控制进程并获取有关该进程的信息。 Process类是抽象类&#xff0c;因此无法实例化。 Process类的重要方法是Destroy()、exitValue()、getErrorStream()、waitFor()、getI…

cscript 执行代码_WSCRIPT与CScript区别

WSCRIPT与CScript区别窗口中运行&#xff0c;WSCRIPT;命令行中运行&#xff0c;CScrip;cscript因为把输出放进了控制台 所以可以对它重定向 方便程序调用wscript直接用窗口输出 适合调试程序和编写小工具 在windows下不用cmd的话cscript的输出会一闪而过 大多数情况下&#xff…

java虚拟机内存模型种类_深入理解volatile类型——从Java虚拟机内存模型角度

一、前言在java多线程编程中&#xff0c;volatile可以用来定义轻量级的共享变量&#xff0c;它比synchronized的使用成本更低&#xff0c;因为它不会引起线程上下文的切换和调度。所谓知己知彼、百战不殆。本文从JVM内存模型角度&#xff0c;探讨volatile的实现原理。在明白了v…

寒武纪芯片创始人:要让AI芯片计算效率提高一万倍

来源&#xff1a; 传感器技术 概要&#xff1a;随着利好政策的不断出台以及行业的持续发展&#xff0c;芯片相关产业站上风口。 随着利好政策的不断出台以及行业的持续发展&#xff0c;芯片相关产业站上风口。而AI芯片独角兽公司寒武纪&#xff0c;也即将入驻雄安新区。e公司记…

SQL模糊查询语句和Escape转义字符

来源&#xff1a;http://www.cnblogs.com/amylis_chen/archive/2010/07/16/1778921.html 通配符 描述 示例 % 包含零个或更多字符的任意字符串。 WHERE title LIKE %computer% 将查找处于书名任意位置的包含单词 computer 的所有书名。 _&#xff08;下划线&#xff09; 任何…

JAVA格式化同一天时间间隔_java中时间格式化怎么去时间间隔值

java中时间格式化怎么去时间间隔值关注:61 答案:2 mip版解决时间 2021-01-12 11:52提问者眼泪↓流到叻心里2021-01-11 20:30比如2015-02-03 13:25:55 与 2015-02-04 15:25:55 之间的时间间隔怎么获取最佳答案二级知识专家守給你的承諾2021-01-11 21:46转换成秒-相减取绝对值。…

超美国11倍!麦肯锡176页报告解读数字中国领先全球的秘密

来源&#xff1a;智东西 概要&#xff1a;到2030年&#xff0c;数字化将转变并创造高达45%的行业总收入。 12月4日&#xff0c;麦肯锡发布了长达176页的中国数字经济报告。 报告显示&#xff0c;中国电子商务&#xff08;2016年交易额占全球40%&#xff09;和数字支付&#xf…

SQL Server查询中特殊字符的处理方法

来源&#xff1a;http://database.51cto.com/art/201009/227766.htm SQL Server查询中&#xff0c;经常会遇到一些特殊字符&#xff0c;比如单引号“”等&#xff0c;这些字符的处理方法&#xff0c;是SQL Server用户都应该需要知道的。 我们都知道SQL Server查询过程中&…

JAVA发送邮件工具包_java mail 发送邮件工具包

Java测试发送邮件的时候经常出现异常&#xff0c;检查多次账户名密码无误后&#xff0c;依然出现如下图错误&#xff1a; 之前不清楚&#xff0c;后面在网上得到提醒&#xff0c;我们所使用的smtp服务器使用的密码与我们自身登录网页端邮箱的密码是不一样的&#xff0c;以163邮…

卡内基梅隆大学提出基于学习的动作捕捉模型,用自监督学习实现人类3D动作追踪

原文来源&#xff1a;Cornell University Library 作者&#xff1a;Hsiao-Yu Fish Tung、Hsiao-Wei Tung、Ersin Yumer、 Katerina Fragkiadaki 「雷克世界」编译&#xff1a;我是卡布达、哆啦A亮 目前&#xff0c;从单摄像头中进行动作捕捉&#xff08;motioncapture&#xff…

session过期时间控制的一些常用方法

来源&#xff1a;http://blog.csdn.net/luo_yifan/article/details/4659392 在一般系统登录后&#xff0c;都会设置一个当前session失效的时间&#xff0c;以确保在用户没有使用系统一定时间后&#xff0c;自动退出登录&#xff0c;销毁session。 具体设置很简单&#xff1a…

java 昵称1到32位字符_Java期末考试编程题复习

在程序中定义Person类&#xff0c;为该类编写如下字段、构造器、访问器、修改器和相应的其他方法。(20分)<1>在Person类中定义两个字段&#xff1a; 私有访问权限&#xff0c;类型为String的name字段&#xff1b; 私有访问权限&#xff0c;类型为int的age字段。<2>…

AI智商评测标准专家研讨会邀请,2018年12月20日北京

21世纪以来&#xff0c;人工智能领域陆续爆发很多重要事件。其中最吸引人们眼球的&#xff0c;当属2016年战胜了人类围棋冠军并开始能够从0自我学习的AlphaGo。 10月26日&#xff0c;软银CEO孙正义在沙特阿拉伯举行的未来投资计划大会上称&#xff0c;未来30年内AI的智商&#…

java代码pingip_java程序ping IP

package com.fdway.tool;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;/**** ping IP地址检查IP地址能否可用**/public class PingIPUtil {public static void main(String[] args) throws IOExce…

HTML 显示特殊字符时转义操作

/*** 转换 HTML 特殊字符*/public static String escape( String s ){if( StringUtils.isBlank( s ) ){return " ";}StringBuilder sb new StringBuilder( s.length() );for( int i 0 ; i < s.length() ; i ){char c s.charAt( i );switch( c ){case < : sb…

开源神经网络框架Caffe2全介绍

本文作者吴逸鸣&#xff0c;整理自作者在GTC China 2017大会上的演讲&#xff0c;首发于作者的知乎文章。 我个人认为这是一份很值得分享的资料&#xff0c;因为 这应该是第一次使用全中文来讲解Caffe2和FB的AI应用的演讲 观看这次演讲不需要机器学习&#xff0f;神经网络&…

JAVA设置jscrollbar大小_JScrollBar垂直/水平设置问题 – Java Swing

下面是我上面提到的一个小的可编辑代码片段.我同意camickr你不应该使用绝对定位而是使用布局管理器.如果你绝对需要为JTextField提供一个水平滚动条,那么让它工作的一种方法就是让它始终显示,使用允许这个的JScrollPane构造函数.即,JScrollPane inputPane new JScrollPane(inp…

TPTP(Java Profiling Tools插件)对Java程序进行性能测试

来源&#xff1a;http://blog.csdn.net/wsj19890201/article/details/5492517 TPTP下载地址&#xff1a;http://www.eclipse.org/tptp/ 概述&#xff1a;Eclipse Test & Performance Tools Platform (TPTP) 的性能测试工具 (Profiling tool)能够用于优化eclipse插件应用、…

AI指数评论:提防“路灯谬误”,开启全球多方对话

来源&#xff1a;亿欧 概要&#xff1a;技术的单点突破&#xff0c;资本市场的多级繁荣&#xff0c;诸如此类的话题充斥着过去两到三年。然而&#xff0c;在AI时代思考AI&#xff0c;我们需要更宏大的世界观。 技术的单点突破&#xff0c;资本市场的多级繁荣&#xff0c;诸如此…

算法还是算力?周志华微博引爆深度学习的“鸡生蛋,蛋生鸡”问题

来源&#xff1a;AI科技大本营上周&#xff0c;由强化学习加持的AlphaZero&#xff0c;把DeepMind在围棋上的突破成功泛化到其他棋类游戏&#xff1a;8小时打败李世石版AlphaGo&#xff0c;4小时击败国际象棋最强AI——Stockfish&#xff0c;2小时干掉日本象棋最强AI——Elmo&a…