Java单实例的最佳写法
前言:
代码简洁与性能高效无法两全其美,本文章专注于大并发程序的性能,如果您追求代码简洁,本文章可能不太适合,因为本文章主要讨论如何写出在高并发下也能运行很好的代码。
并文章属于Java并发编程实战中例子。但结合实际场景进行了阐述。
通常,我们如果写一个单实例模式的对象,一般会这样写:
写法一:
public class Singleton {
private static final Singleton instance = new Singleton();
/**
* 防止其他人new对象
*/
private Singleton(){
System.out.println("init");
}
public static Singleton getInstance(){
return instance;
}
}
这种方式叫饥饿式单实例,意思是说,不管你用不用这个类的方法,我都把这个类需要的一切资源都分配好。但这样写有一个问题,就是如果这类需要的资源比较多,在系统启动的时候,就会很慢。
因此要求有懒汉式单实例,于是就出现了第二中写法,
写法二:
public class Singleton {
private static Singleton instance = null;
/**
* 防止其他人new对象
*/
private Singleton(){
System.out.println("init");
}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
这种方式叫懒汉式单实例,即通常所说的延迟加载。这样,在系统启动的时候,不会加载类所需要的各种资源,只有真正使用的时候才去加载各种资源。
但这种方法马上就可以看出问题,因为在多线程情况下,可能会导致重复初始化的问题(不明白这个道理,那您需要补充一下同步及多线程知识了)。于是有了改进版,即目前网上比较流行的写法。
写法三:
public class Singleton {
private static Singleton instance = null;
/**
* 防止其他人new对象
*/
private Singleton(){
System.out.println("init");
}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
加上关键字synchronized,可以保证只有一个线程在执行这个方法。这个方法至此应该说是比较完美的了,但是,专家不这么认为,在高并发多线程的访问系统中,synchronized关键字会让程序的吞吐量急剧下降,因此,在高并发系统中,应该尽量避免使用synchronized锁。
但这并不能难住我们聪明的软件工程师,有人便写出了双重锁的程序。方法如下:
写法四:
这样,通常获得单实例引用是没有锁的,只有第一次初始化时才会加锁,而且如果多个线程进入临界区区后,理论上只有第一个进入临界区的线程才会初始化对象,之后进入临界区的线程因为之前的线程已经初始化,就不会再次进行初始化。
但专家怎么说呢?这个代码有问题。首先,这个程序对同步的应用很到位,即当进入synchronied区,只有一个线程在访问Singleton类。但却忽略了变量的可见性。因为在没有同步的保护下,instance的值在多个线程中可能都是空的,因为即便第一个线程对类进行了初始化,并把类的引用赋值给了instance变量,但也不能保证instance变量的值对其他线程是可见的,因为变量instance没有采用同步的机制。
在java5之后,可以在instance前面添加volatile关键字来解决这个问题,但是这种双重锁的方式已经不建议使用。
那么,看看大师推荐的写法吧,见 Java Concurrency In Practice的List 16.6代码:
写法五:
综上各种写法,发现写法一虽然在启动时会让系统启动的慢一些,但却不失为一种高效的写法,当然,如果确实对系统启动时的速度要求高的话,则应该考虑写法五了。
Java单实例的最佳写法的更多相关文章
- Java ,单实例 多线程 ,web容器,servlet与struts1-2.x系列,线程安全的解决
1.Servlet是如何处理多个请求同时访问呢? 回答:servlet是默认采用单实例,多线程的方式进行.只要webapp被发布到web容器中的时候,servlet只会在发布的时候实例化一次,serv ...
- Java中的单实例
前几天刚学完单实例设计模式,今天看代码时发现一行代码很奇怪,getRuntime()函数的返回类型怎么是它本身,忽然想起前几天学的单实例模式,于是找到方法的定义,果然是静态私有变量,获取实例的公有方法 ...
- 【java】单实例下的 流水号【21位】
单实例环境,不是分布式 需要流水号 /** * 流水号生成器 * * 年+天号+毫秒+随机数 * 2019+134+480+11位随机数 * 4+3+3+11 = 21位 * * * @author ...
- java单例模式的几种写法比较
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
- java单例-积木系列
一步步知识点归纳吧,把以前似懂非懂,了解表面,知道点不知道面的知识归一下档. 懒汉式单例: 私有化构造函数,阻止外界实例话对象,调用getInstance静态方法,判断是否已经实例化. 为什么是懒 ...
- Servlet单实例多线程模式
http://kakajw.iteye.com/blog/920839 前言:Servlet/JSP技术和ASP.PHP等相比,由于其多线程运行而具有很高的执行效率.由于Servlet/JSP默认是以 ...
- 【Java学习笔记之三十】详解Java单例(Singleton)模式
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
- 转:java单例设计模式
本文转自:http://www.cnblogs.com/yinxiaoqiexuxing/p/5605338.html 单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton ...
- zookeeper的单实例和伪集群部署
原文链接: http://gudaoyufu.com/?p=1395 zookeeper工作方式 ZooKeeper 是一个开源的分布式协调服务,由雅虎创建,是 Google Chubby 的开源实现 ...
随机推荐
- LNMP一键安装包-CentOS 5/6下自动编译安装Nginx,MySQL,PHP
适用环境: 系统支持:CentOS-5 (32bit/64bit).CentOS-6 (32bit/64bit) 内存要求:≥128M 安装了什么: 1.Nginx-1.2.0 2.MySQL 5.5 ...
- 使用开源库PhotoView
一.下载地址:https://github.com/chrisbanes/PhotoView 点击DownLoad ZIP,进行下载. 二.解压,然后复制PhotoView-master\librar ...
- B/S的验证控件
验证控件 首先设置一下框架,设置为.net framework 4.0,在4.5下貌似会报错,设置方法为项目上右键/属性页/找到左侧菜单栏里的生成/将框架版本改为4.0. 一.非空验证:Require ...
- JavaScript高级程序设计(第三版)学习笔记20、21、23章
第20章,JSON JSON(JavaScript Object Notation,JavaScript对象表示法),是JavaScript的一个严格的子集. JSON可表示一下三种类型值: 简单值: ...
- EXT.NET学习笔记(一) 下载配置使用
新公司使用ext.net开发,开始学习该知识: 首先下载ext.net,目前我使用的版本为1.7,该版本免费,基本的功能也够用,使用ext.net进行开发时强烈建议使用VS2015,能便捷的提示,大大 ...
- java.lang.RuntimeException: Unable to instantiate activity ComponentInfo异常总结
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo异常总结 做android开发的可能都碰到"j ...
- sqlserver中的聚合函数
聚合函数:就是按照一定的规则将多行(Row)数据汇总成一行的函数,对数据进行汇总前,还可以按特定的列(coloumn)将数据进行分组(group by)再汇总,然后按照再次给定的条件进行筛选 一:Co ...
- JavaScript高级程序设计(九):基本概念----语句的特殊点
一.Label语句.break/continue语句和for循环语句的结合使用: 1.Label语句可以在代码中添加标签,以便将来使用.语法: label:statment eg: start:f ...
- IO流07_输入输出流总体系
[javaIO体系中常用的流] [关于字符流和字节流的注意点] 通常,字节流比字符流功能更加强大,因为字节流可以处理所有的二进制文件. 但是字节流来处理字符,又需要将字节转换成字符,增加了编程复杂度. ...
- 04_线程的创建和启动_使用Callable和Future的方式
[简述] 从java5开始,java提供了Callable接口,这个接口可以是Runnable接口的增强版, Callable接口提供了一个call()方法作为线程执行体,call()方法比run() ...