单例模式在实际开发中有很多的用途,比如我们在项目中常用的工具类,数据库等资源的连接类。这样做的好处是避免创建多个对象,占用内存资源,自始自终在内存中只有一个对象为我们服务。

单例对象一般有两种实现方式。懒汉式和饿汉式。

饿汉式实现如下:

package com.day05;

/**
* 饿汉式
*
* @author Administrator
*
*/
public class Single {
// 定义一个个私有静态本类对象变量,饿汉式是一上来就给该变量进行初始化,加上final是让s这个对象引用始终保持不变,更为严谨
private static final Single s = new Single(); // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
private Single() {
} // 暴露出一个外界可以获取该类对象的公共静态方法
public static Single getInstance() {
return s;
} }

测试类:

package com.day05;

public class SingleDemo {
public static void main(String[] args) {
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1==s2); }
}

运行结果:

true
即s1==s2说明了,s1和s2在内存中地址都相等,即s1、和s2是同一个对象。

懒汉式实现如下:

package com.day05;

/**
* 懒汉式
*
* @author Administrator
*
*/
public class Single {
// 定义一个个私有静态本类对象变量,懒汉式是先赋值为null,当需要的时候在初始化
private static Single s = null; // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
private Single() {
} // 暴露出一个外界可以获取该类对象的公共静态方法
public static Single getInstance() {
if (s == null)
s = new Single();
return s;
} }

以上的代码如果是单线程的话就不会存在问题,但是当有多线程操作的时候,就会存在线程安全问题,演示代码如下:

package com.day05;

/**
* 懒汉式
*
* @author Administrator
*
*/
public class Single {
// 定义一个个私有静态本类对象变量,懒汉式是先赋值为null,当需要的时候在初始化
private static Single s = null; // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
private Single() {
} // 暴露出一个外界可以获取该类对象的公共静态方法
public static Single getInstance() {
if (s == null) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
s = new Single();
}
return s;
} }

测试类如下:

package com.day05;

public class SingleDemo implements Runnable {

    public static void main(String[] args) {
SingleDemo sd = new SingleDemo();
new Thread(sd).start();
new Thread(sd).start(); } @Override
public void run() {
Single s = Single.getInstance();
System.out.println(s); }
}

运行结果如下:

com.day05.Single@4081b5a4
com.day05.Single@64dcdaac

可以发现每次运行结果打印出获取对象不是同一个,即存在线程安全问题。

问题分析:

由此我们可以采用Java给我们提供的同步锁来解决以上的问题,修改代码如下:

package com.day05;

/**
* 懒汉式
*
* @author Administrator
*
*/
public class Single {
// 定义一个个私有静态本类对象变量,懒汉式是先赋值为null,当需要的时候在初始化
private static Single s = null; // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
private Single() {
} // 暴露出一个外界可以获取该类对象的公共静态方法
public static synchronized Single getInstance() {
if (s == null) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
s = new Single();
}
return s;
} }

这样就解决了上面的代码存在的线程安全问题,但是同步锁虽然可以解决了线程安全问题,但是却会存在效率问题,所以我们可以采用双重判断的方法来优化一下改代码如下所示:

package com.day05;

/**
* 懒汉式
*
* @author Administrator
*
*/
public class Single {
// 定义一个个私有静态本类对象变量,懒汉式是先赋值为null,当需要的时候在初始化
private static Single s = null; // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
private Single() {
} // 暴露出一个外界可以获取该类对象的公共静态方法
// 这里需要注意的使静态共享数据使用的使该类的字节码对象即Single.class
public static Single getInstance() {
// 这里增加了一次判断,可以少一次进行锁的处理
if (s == null) {
synchronized (Single.class) {
if (s == null) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
s = new Single();
}
}
} return s;
} }

总结:还是比较推荐使用饿汉式,因为写法简单,不存在线程安全问题。

Java实现单例模式的两种方式的更多相关文章

  1. 对Java代码加密的两种方式,防止反编译

    使用Virbox Protector对Java项目加密有两种方式,一种是对War包加密,一种是对Jar包加密.Virbox Protector支持这两种文件格式加密,可以加密用于解析class文件的j ...

  2. Java新建线程的两种方式

    Java新建线程有两种方式,一种是通过继承Thread类,一种是实现Runnable接口,下面是新建线程的两种方式. 我们假设有个竞赛,有一个选手A做俯卧撑,一个选手B做仰卧起坐.分别为两个线程: p ...

  3. Java实现多线程的两种方式

    实现多线程的两种方式: 方式1: 继承Thread类 A: 自定义MyThread类继承Thread类 B: 在MyThread类中重写run() C: 创建MyThread类的对象 D: 启动线程对 ...

  4. [Java] HashMap遍历的两种方式

    Java中HashMap遍历的两种方式原文地址: http://www.javaweb.cc/language/java/032291.shtml第一种: Map map = new HashMap( ...

  5. Java实现深克隆的两种方式

    序列化和依次克隆各个可变的引用类型都可以实现深克隆,但是序列化的效率并不理想 下面是两种实现深克隆的实例,并且测试类对两种方法进行了对比: 1.重写clone方法使用父类中的clone()方法实现深克 ...

  6. java文件读写的两种方式

    今天搞了下java文件的读写,自己也总结了一下,但是不全,只有两种方式,先直接看代码: public static void main(String[] args) throws IOExceptio ...

  7. K:java中序列化的两种方式—Serializable或Externalizable

    在java中,对一个对象进行序列化操作,其有如下两种方式: 第一种: 通过实现java.io.Serializable接口,该接口是一个标志接口,其没有任何抽象方法需要进行重写,实现了Serializ ...

  8. java 实现websocket的两种方式

    简单说明 1.两种方式,一种使用tomcat的websocket实现,一种使用spring的websocket 2.tomcat的方式需要tomcat 7.x,JEE7的支持. 3.spring与we ...

  9. java实现同步的两种方式

    同步是多线程中的重要概念.同步的使用可以保证在多线程运行的环境中,程序不会产生设计之外的错误结果.同步的实现方式有两种,同步方法和同步块,这两种方式都要用到synchronized关键字. 给一个方法 ...

随机推荐

  1. GIT基本概念和用法总结【转】

    在日常使用GIT过程中,经常会出错,比如无意间丢失了未提交的数据,回退版本时丢失了工作目录,等等.经过思考发现,所有这些错误都是因为对GIT中一些基本的概念模糊而导致,因为对一些基本概念不清晰,导致对 ...

  2. Jenkins spring boot 自动部署方案

    原文地址:http://www.cnblogs.com/skyblog/p/5632869.html 现在主流的自动部署方案大都是基于Docker的了,但传统的自动部署方案比较适合中小型公司,下面的方 ...

  3. JS倒计时效果

    [html] <div id="time"></div> <script> var pad = function(num){ return nu ...

  4. nodejs 获取指定路径下所有的文件夹名

    示例:获取 ./components 下所有的文件夹名称 let components = [] const files = fs.readdirSync('./components') files. ...

  5. Jquery全选与反选点击执行一次的解决方案

    在做项目时遇到一个bug,checkbox全选与反选功能,只能点击一次,再点就不起作用了,为了解决此问题,我查找了好多资料,下面把具体解决方案整理分享给大家,需要的朋友可以参考下: 代码需求, 使用a ...

  6. Linux下(centos6.8)JDK1.8的安装与配置

    今天说下在Linux(centos6.8)系统下的JDK安装与配置. 据我所知的jdk安装方式有三种(rpm.yum方式没用过,暂且不提)今天只说解压安装方式: 一.解压jdk安装包: 附上jdk1. ...

  7. 论C++STL源代码中关于堆算法的那些事

    关于堆,我们肯定熟知的就是它排序的时间复杂度在几个排序算法里面算是比較靠上的O(nlogn)常常会拿来和高速排序和归并排序讨论,并且它还有个长处是它的空间复杂度为O(1), 可是STL中没有给我们提供 ...

  8. Atitit.js跨域解决方案attilax大总结 后台java php c#.net的CORS支持

    Atitit.js跨域解决方案attilax大总结 后台java php c#.net的CORS支持 1. 设置 document.domain为一致  推荐1 2. Apache 反向代理 推荐1 ...

  9. 用C#实现XML和实体类之间序列化和反序列化相互转换

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...

  10. mysql数据库记录

    ON DELETE restrict(约束):当在父表(即外键的来源表)中删除对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除. no action:意思同restrict.即如果存在从数 ...