一、概述:
单例模式:某个类在整个系统种只能有一个实例对象可被获取和使用的代码模式,提供一个全局唯一的类的实例对象给外部调用,例如:代表jvm运行环境的Runtime类。
要点如下:
1、保证类只有唯一一个实例对象:则要求存储该实例对象的是一个静态变量;
2、保证只能在该类的内部创建实例对象,其他地方不允许创建:则要求该类的构造器私有化;
3、保证外部能访问获取到这个唯一的实例对象:
1)直接暴露public
2)提供静态变量的get方法。
4、根据创建对象时机的不同,分饿汉式和懒汉式:
1)饿汉式:还没等到要用的时候,类加载的时候就生成;
2)懒汉式:第一次实际要使用这个实例对象的时候才生成。
二、饿汉式单例模式有三种实现方式:
1、静态常量直接实例化(代表jvm运行环境的Runtime类生成单例,采用的就是这种实现方式,简洁直观);
2、枚举类(同1,虽然写法不同,但是实际原理相同,最简洁);
3、静态代码块(适合复杂的场景,如从配置文件读取所需参数)。
总结:饿汉式单例模式,由于在类加载的时候就完成实例化,所以避免了线程同步问题(运行期)。
三、懒汉式单例模式有三种实现方式:
1、懒汉式版本1(非线程安全);
2、懒汉式版本2(同步方法实现线程安全,但效率很低);
3、懒汉式版本3(同步代码块实现线程安全,但效率依旧较低);
4、懒汉式版本4(DCL(Double Check Lock),线程安全效率较高,但由于synchronized无法避免jvm优化时可能出现因指令重排导致的乱序执行,可能会遇到拿到的实例对象尚未被正确初始化的问题,这时可以利用volatile阻止指令重排的特点来解决);
5、懒汉式版本5 静态内部类(内部类被加载和初始化时才会创建对象,即调用时 )
总结:饿汉式单例模式,由于不是在类加载的时候就完成实例化,所以有可能会出现线程同步问题(运行期),需要设法解决。
四、饿汉式代码 Hungry.java
/1、静态常量直接实例化(代表jvm运行环境的Runtime类生成单例,采用的就是这种实现方式,简洁直观);
class HungrySigleton1 {
private HungrySigleton1(){
}
private final static HungrySigleton1 INSTANCE = new HungrySigleton1();
public static HungrySigleton1 getInstance(){
return INSTANCE;
}
}
//2、枚举类(最简洁);
enum HungrySigleton3{
INSTANCE;
private HungrySigleton3() {
}
}
//3、静态代码块(适合复杂的场景,如从配置文件读取所需参数)。
class HungrySigleton2 {
private HungrySigleton2(String info){
System.out.println(info);}
private static HungrySigleton2 INSTANCE =null;
static {
Properties prop = new Properties();
InputStream inStream = HungrySigleton2.class.getClassLoader().getResourceAsStream("project.properties");
try {
prop.load(inStream);} catch (IOException e) {
e.printStackTrace();}
String info =(String) prop.get("info");
//扩展:读取配置文件的几种方式 1、Properties继承自Hashtable 2、ResourceBundle 3、Spring种利用ApplicationContext加载xml文件
INSTANCE = new HungrySigleton2(info);
}
public static HungrySigleton2 getInstance(){
return INSTANCE;
}
}
public class Hungry{
public static void main(String[] args) {
HungrySigleton1 ins1 = HungrySigleton1.getInstance();
HungrySigleton2 ins2 = HungrySigleton2.getInstance();
HungrySigleton3 ins3 = HungrySigleton3.INSTANCE;
}
}
五、懒汉式代码 Lazy.java
class LazySigleton1 {
private LazySigleton1(){
}
private static LazySigleton1 INSTANCE =null;
public static LazySigleton1 getInstance(){
if(null == INSTANCE){
INSTANCE = new LazySigleton1();
}
return INSTANCE;
}
}
//2、懒汉式版本2(同步方法实现线程安全,但效率很低);
class LazySigleton2 {
private LazySigleton2(){
}
private static LazySigleton2 INSTANCE =null;
public synchronized static LazySigleton2 getInstance(){
if(null == INSTANCE){
try {
Thread.sleep(100);} catch (InterruptedException e) {
e.printStackTrace();}
INSTANCE = new LazySigleton2();
}
return INSTANCE;
}
}
//3、懒汉式版本2(同步代码块提供了一定效率,但存在线程安全问题);
class LazySigleton3 {
private LazySigleton3(){
}
private static LazySigleton3 INSTANCE =null;
public static LazySigleton3 getInstance(){
if(null == INSTANCE){
try {
Thread.sleep(100);} catch (InterruptedException e) {
e.printStackTrace();}
synchronized(INSTANCE){
INSTANCE = new LazySigleton3();
}
}
return INSTANCE;
}
}
//4、懒汉式版本2(DCL(Double Check Lock),线程安全效率较高,但由于synchronized无法避免jvm优化时可能出现的乱序执行,可能会遇到拿到的实例对象尚未被正确初始化的问题,这时可以利用volatile来解决);
class LazySigleton4 {
private LazySigleton4(){
}
private static volatile LazySigleton4 INSTANCE =null;
public static LazySigleton4 getInstance(){
if(null == INSTANCE){
try {
Thread.sleep(100);} catch (InterruptedException e) {
e.printStackTrace();}
synchronized(INSTANCE){
if(null == INSTANCE){
INSTANCE = new LazySigleton4();
}
}
}
return INSTANCE;
}
}
//5、静态内部类(内部类被加载和初始化时才会创建对象,即调用时)
class LazySigleton5 {
private LazySigleton5(){
}
private static class Inner{
private final static LazySigleton5 INSTANCE = new LazySigleton5();
}
public static LazySigleton5 getInstance(){
return Inner.INSTANCE;
}
}
public class Lazy {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService exec = Executors.newFixedThreadPool(2);
Callable task = new Callable(){
@Override
public Object call() {
LazySigleton1 instance = LazySigleton1.getInstance();
System.out.println(Thread.currentThread().getName()+instance);
return instance;
}
};
Future f1 = exec.submit(task);
Future f2 = exec.submit(task);
LazySigleton1 l1 = (LazySigleton1) f1.get(),l2 = (LazySigleton1) f2.get();
System.out.println("l1:" + l1);
System.out.println("l2:" + l2);
System.out.println(l1==l2);
exec.shutdown();
}
}