单例设计模式
之前文章有提到过单例设计模式的初步介绍:Java设计模式之单例设计模式
下文提到的枚举:Java中的枚举类是什么?enum关键字怎么使用?
涉及到的:Java多线程09—实现Callable接口创建线程
下文懒汉式里面的同步方法解决线程问题:Java多线程04—同步方法解决线程的安全问题
什么是Singleton?
- 在Java中指单例设计模式,它是软件开发中最常用的设计模式之一,单:唯一;例:实例
- 单例设计模式:即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式
- 例如:代表jvm运行环境的Runtime类
要点
一是某个类只能有一个实例
- 构造器私有化
二是它必须自行创建这个实例
- 含有一个该类的静态变量来保存这个唯一的实例
三是它必须自行向整个系统提供这个实例
对外提供直接获取该实例对象的方式:
- 直接暴露
- 用静态变量的get方法获取
几种常见的方式
饿汉式
在类初始化时直接创建对象,不存在线程安全问题
直接实例化饿汉式(简洁直观)
/*
饿汉式:直接创建实例对象,不管是否需要这个对象,都会创建
(1):构造器私有化
(2):自行创建,并且用静态变量保存
(3):向外提供这个单例
(4):强调这是一个单例,我们可以用final修改
*/
public class Singleton01 {
public static final Singleton01 INSTANCE = new Singleton01();
private Singleton01() {
}
}
枚举式(最简洁)
/*
枚举类型:表示该类型的对象是有限的几个,所以可以限定为一个,就成了单例
*/
public enum Singleton02 {
INSTANCE
}
静态代码块饿汉式(适合复杂实例化)
public class Singleton03 {
public static final Singleton03 INSTANCE;
static {
INSTANCE=new Singleton03();
}
private Singleton03(){
}
}
测试
/*
枚举类型打印出来的数据不一样,因为枚举是重写了toString方法,打印出来的是对象的名字
*/
public class TestSingleton {
public static void main(String[] args) {
Singleton01 instance1 = Singleton01.INSTANCE;
System.out.println(instance1);//SingletonTest.Singleton01@6d6f6e28
Singleton02 instance2 = Singleton02.INSTANCE;
System.out.println(instance2);//INSTANCE
}
}
懒汉式
延迟创建对象
线程不安全(适用于单线程)
/*
懒汉式:
延迟创建这个实例对象
1.构造器私有化
2.用一个静态变量保存这个唯一的实例
3.提供一个静态方法,获取这个实例对象
*/
public class Singleton04 {
private static Singleton04 instance;
private Singleton04(){
}
public static Singleton04 getInstance(){
if(instance==null){
instance=new Singleton04();
}
return instance;
}
}
单线情况下测试:
public class TestSingleton02 {
public static void main(String[] args) {
//单线情况下
Singleton04 singleton01=Singleton04.getInstance();
Singleton04 singleton02=Singleton04.getInstance();
//测试拿到的是不是同一个对象
System.out.println(singleton01==singleton02);//true
}
}
线程安全(适用于多线程)
- 可能不安全
import java.util.concurrent.*;
public class TestSingleton02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//单线情况下
// Singleton04 singleton01=Singleton04.getInstance();
// Singleton04 singleton02=Singleton04.getInstance();
// System.out.println(singleton01==singleton02);//true
//线程安全问题
Callable<Singleton04> c = new Callable<Singleton04>(){
@Override
public Singleton04 call() throws Exception {
return Singleton04.getInstance();
}
};
ExecutorService es = Executors.newFixedThreadPool(2);
Future<Singleton04> f1 = es.submit(c);
Future<Singleton04> f2 = es.submit(c);
Singleton04 s1 = f1.get();
Singleton04 s2 = f2.get();
System.out.println(s1);
System.out.println(s2);
System.out.println(s2==s1);
//运行结果有概率问题,可能false可能true
es.shutdown();
}
}
解决办法:同步方法synchronized
类Singleton05
//学习内容:
//开发时间:10月30日 21:38
package SingletonTest02;
/*
懒汉式:
延迟创建这个实例对象
1.构造器私有化
2.用一个静态变量保存这个唯一的实例
3.提供一个静态方法,获取这个实例对象
*/
public class Singleton05 {
private static Singleton05 instance;
private Singleton05(){
}
public static Singleton05 getInstance(){
synchronized (Singleton05.class){//当前类为锁对象
if(instance==null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance=new Singleton05();
}
}
return instance;
}
}
测试类:TestSingleton022
import java.util.concurrent.*;
public class TestSingleton022 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//单线情况下
// Singleton04 singleton01=Singleton04.getInstance();
// Singleton04 singleton02=Singleton04.getInstance();
// System.out.println(singleton01==singleton02);//true
//线程安全问题
Callable<Singleton05> c = new Callable<Singleton05>(){
@Override
public Singleton05 call() throws Exception {
return Singleton05.getInstance();
}
};
ExecutorService es = Executors.newFixedThreadPool(2);
Future<Singleton05> f1 = es.submit(c);
Future<Singleton05> f2 = es.submit(c);
Singleton05 s1 = f1.get();
Singleton05 s2 = f2.get();
System.out.println(s1);
System.out.println(s2);
System.out.println(s2==s1);
es.shutdown();
}
}
静态内部类形式(适用于多线程)
既能保持延迟加载,还能保证线程安全(看注释)
/*
在内部类被加载和初始化时,才创建INSTANCE实例对象
静态内部类不会随着外部类的加载和初始化而初始化,它是要单独去加载和初始化
因为是在内部类加载和初始化时,创建的,因此是线程安全的
*/
public class Singleton07 {
public Singleton07() {
}
private static class Inner{
private static final Singleton07 INSTANCE=new Singleton07();
}//内部类
public static Singleton07 getInstance2(){
return Inner.INSTANCE;
}
}