【JAVA单例模式详解】
设计模式是一种思想,适合于任何一门面向对象的语言。共有23种设计模式。
单例设计模式所解决的问题就是:保证类的对象在内存中唯一。
举例:
A、B类都想要操作配置文件信息Config.java,所以在方法中都使用了Config con=new Config();但是这是两个不同的对象。对两者的操作互不影响,不符合条件。
解决思路:
1.不允许其他程序使用new创建该类对象。(别人new不可控)
2.在该类中创建一个本类实例。
3.对外提供一个方法让其他程序可以获取该对象。
解决方法:单例模式。
步骤:
1.私有化该类的构造函数
2.通过new在本类中创建一个本类对象。
3.定义一个共有的方法将创建的对象返回。
一、饿汉式
雏形:
class Single
{
private static final Single s=new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
为什么方法是静态的:不能new对象却想调用类中方法,方法必然是静态的,静态方法只能调用静态成员,所以对象也是静态的。
为什么对象的访问修饰符是private,不能是public 吗?不能,如果访问修饰符是Public,则Single.s也可以得到该类对象,这样就造成了不可控。
举例:
public class Single
{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private static final Single s=new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
public class Main {
public static void main(String args[])
{
Single s=Single.getInstance();
s.setName("张三");
System.out.println(s.getName());
}
}
最后结果为:张三
二、懒汉式
前面的是饿汉式单例模式,下面开始讲解懒汉式单例模式。
class Single
{
private static Single s=null;
private Single(){}
public static Single getInstance()
{
if(s==null)
s=new Single();
return s;
}
}
我们可以看到懒汉式和饿汉式相比的区别就是懒汉式创建了延迟对象同时饿汉式的实例对象是被修饰为final类型。
懒汉式的好处是显而易见的,它尽最大可能节省了内存空间。
但是懒汉式又有着弊端,在多线程编程中,使用懒汉式可能会造成类的对象在内存中不唯一,虽然通过修改代码可以改正这些问题,但是效率却又降低了。而且如果想要使用该类对象,就必须创建对象,所以虽然貌似使用懒汉式有好处,但是在实际开发中使用的并不多。
总结:
懒汉式在面试的时候经常会被提到,因为知识点比较多,而且还可以和多线程结合起来综合考量。
饿汉式在实际开发中使用的比较多。
三、懒汉式在多线程中的安全隐患以及解决方案、优化策略。
下面分析懒汉式在多线程中的应用和出现的问题以及解决方法。
懒汉式在多线程中出现的问题:
懒汉式由于多加了一次判断
if(s==null)
导致了线程安全性隐患。因为CPU很有可能在执行完if语句之后切向其它线程。解决线程安全性问题的关键就是加上同步锁。
1.使用同步函数
我们可以直接使用同步函数:
class Single
{
private static Single s=null;
private Single()
{
}
public static synchronized Single getInstance()
{
if(s==null)
s=new Single();
return s;
}
}
但是直接使用同步函数的方法效率十分低下,因为每次调用此方法都需要先判断锁。
2.使用同步代码块
我们也可以使用同步代码块:
class Single
{
private static Single s=null;
private Single()
{ }
public static Single getInstance()
{
synchronized(Single.class)
{
if(s==null)
s=new Single();
return s;
}
}
}
但是每次调用getInstance方法仍然会判断锁,事实上没有改变效率问题。
3.最终解决方案
我们可以使用另外一种方式,达到只判断一次锁,并且实现同步的目的:
class Single
{
private static Single s=null;
private Single()
{ }
public static Single getInstance()
{
if(s==null)//和上面的相比只是多增加了一次判断
{
synchronized(Single.class)
{
if(s==null)
s=new Single();
return s;
}
}
return s;
}
}
观察代码可以发现和上面的代码相比,只是增加了一次判断而已,但是,这一次判断却解决了效率问题。
我们可以分析一下这个代码:
4.最终解决方案代码分析和总结
假设我们现在并没有创建单例对象,即s==null,那么我们调用getInstance方法的时候,会进入if块,然后进入同步代码块,此时,别的线程如果想要创建Single实例,就必须获取锁;等当前线程创建完实例对象,释放锁之后,假设正巧有几个线程已经进入了if块中,它们会拿到锁,进入同步代码块,但是由于进行了判空操作,所以不会创建Single实例,而是直接返回已经创建好的Single实例。如果有多个其他线程进入了if块,当它们依次进入同步代码块的时候,同理也不会创建新的Single实例。而没有进入if块的线程,判空操作之后不满足条件,进不了if块,而直接执行了下一条语句return s;其后的线程调用getInstance方法时,只会判断一次s==null,不满足条件直接返回Single单例s,这样就大大提高了了执行效率。
总结:在代码
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s=new Single();
return s;
}
}
return s;
中,第一行代码是第一次判空操作,目的是提高效率;第三行代码是同步代码块的入口,目的是保证线程安全;第五行代码进行第二次判空操作是为了保证单例对象的唯一性
【JAVA单例模式详解】的更多相关文章
- 9种Java单例模式详解(推荐)
单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象. 懒汉式(线程不安全) 其主要表现在单例类在外 ...
- Java 单例模式详解
概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...
- Java 单例模式详解(转)
概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...
- 9种Java单例模式详解
单例模式的特点 一个类只允许产生一个实例化对象. 单例类构造方法私有化,不允许外部创建对象. 单例类向外提供静态方法,调用方法返回内部创建的实例化对象. 懒汉式(线程不安全) 其主要表现在单例类在外部 ...
- java单例模式详解
饿汉法 饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建.代码如下: public class Singleton { private static Singleton = ne ...
- android java 设计模式详解 Demo
android java 设计模式详解 最近看了一篇设计模式的文章,深得体会,在此基础我将每种设计模式的案例都写成Demo的形式,方便读者研究学习, 首先先将文章分享给大家: 设计模式(Design ...
- Java内部类详解
Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...
- 黑马----JAVA迭代器详解
JAVA迭代器详解 1.Interable.Iterator和ListIterator 1)迭代器生成接口Interable,用于生成一个具体迭代器 public interface Iterable ...
- C++调用JAVA方法详解
C++调用JAVA方法详解 博客分类: 本文主要参考http://tech.ccidnet.com/art/1081/20050413/237901_1.html 上的文章. C++ ...
随机推荐
- Caffe学习系列(8):solver及其配置
solver是caffe的核心. net: "examples/mnist/lenet_train_test.prototxt" test_iter: 100 test_inter ...
- Delphi实现窗体内嵌其他应用程序窗体
实现原理是启动一个应用程序,通过ProcessID得到窗体句柄,然后对其设定父窗体句柄为本程序某控件句柄(本例是窗体内一个Panel的句柄),这样就达成了内嵌的效果. 本文实现的是内嵌一个记事本程序, ...
- [ruby on rails] 跟我学之(10)数据输入验证
这里简单加上几个验证,非空,最小长度,唯一 修改模型 修改app/models/post.rb文件,如下: class Post < ActiveRecord::Base #attr_acces ...
- 转:JQuery实现下拉框的数据加载和联动
<script type="text/javascript"> $(document).ready(function() { GetByJquery(); $(&quo ...
- 求教Sublime Text2 SublimeLinter插件安装问题
昨天装了 SublimeLinter插件(代码语法检测),这个事插件的地址:https://github.com/Kronuz/SublimeLinter 按照作者的介绍配置了一下,发现语法检测不起作 ...
- 【架构】linkerd:来自Twitter为微服务而生的开源RPC解决方案
大家要如何以规模化方式运维微服务应用程序?实践当中会出现哪些问题,我们又该如何加以解决?在大规模与非预测性工作负载场景当中,我们需要满足哪些条件才能运行一款大型微服务应用程序,而又能够确保不必受到功能 ...
- 学 Redux (转)
0.官方文档:http://redux.js.org/ 中文版本 : http://cn.redux.js.org/ 中文版本(好!): http://www.css88.com/react/d ...
- poj 3126
一道搜索的水题,其实搜索相对其他的来说好掌握一点,因为有个固定的模板可以用去套 题目大意就是数字的变化,一个数字只可以变化到它最相邻的一个素数上去,意思是只变化一位数字,求最短的变化方案 #inclu ...
- struts2 复杂参数封装
1.1.1 Struts2中封装复杂类型的数据: 封装到List集合: 页面: 商品名称:<input type="text" name="products[ ...
- 用普通用户通过sudo进行启动tomcat时报如下异常
用普通用户通过sudo进行启动tomcat时报如下异常 tomcat user 不在 sudoers 文件中.此事将被报告. 这是由于sudo命令使用root用户执行命令.而处于安全性的考虑,一般不允 ...