java单例的实现有多种,这里介绍简单的几种:
单例都是要求 构造方法私有化
1:内部类模式(推荐使用)
特点:能实现懒加载、是线程安全的,不用锁实现
/**
* 内部类实现单例
* 0:只初始化一个
* 1:懒加载
* 2:线程安全
*/
public class InnerSingle{
private InnerSingle(){
System.out.println("内部类初始化");
}
public static InnerSingle getInstance(){
return InnerHelper.innerSingle;
}
private static class InnerHelper{
private static InnerSingle innerSingle = new InnerSingle();
}
}
2:饿汉模式 - 怕挨饿,先实例
特点:静态属性中新建对象
优点:线程安全、性能也高 缺点:不能懒加载
/**
* 单例 - 饿汉
*/
public class SingleHungry {
private static SingleHungry singleHungry = new SingleHungry();
private SingleHungry(){
System.out.println("初始化");
}
public static SingleHungry getInstance(){
return singleHungry;
}
}
3:懒汉模式 - 用到再实例
普通模式:不用同步关键字,为线程不安全
if(instance == null){
instance = new Single()
}
方法锁模式:在getInstance方法前加入 同步 关键字得意实现线程安全。
特点:在初始化完成后,每次调用都需要去竞争锁,效率特别慢。
有点:解决了线程安全的问题。
public static synchronized getInstance() {
if(instance == null){
instance = new Single()
}
}
双检查模式(重点学习):
加入volatile保证变量在不同线程间的 可见性
判断有无实例(第一次检查),只有在没有实例前才需要去走同步代码
获取锁后,还需要再次判断是否已经实例化了。(第二次检查,读者先思考下这里为什么要第二次检查,后面有限多例模式有解答)
public class SyncSingle {
/**
* 对保存实例的变量添加volatile的修饰
*/
private volatile static SyncSingle instance = null;
private SyncSingle(){
}
public static SyncSingle getInstance(){
//先检查实例是否存在,如果不存在才进入下面的同步块
if(instance == null){
//同步块,线程安全的创建实例
synchronized(SyncSingle.class){
//再次检查实例是否存在,如果不存在才真的创建实例
if(instance == null){
instance = new SyncSingle();
}
}
}
return instance;
}
}
有限多例模式
有限多例的意思是最多能实例化多少个,仿照单例模式的 懒汉的双检查 方法 来实现。
/**
* 有限的多例 3个
*/
public class ThreeSingle {
private ThreeSingle(){
}
private static volatile List<ThreeSingle> list = new ArrayList<>();
private static final int INSTANCE_MAX = 3;
public static ThreeSingle getInstance(){
if(list.size() < INSTANCE_MAX){
synchronized (ThreeSingle.class){
System.out.println("list size为"+list.size());
if(list.size() < INSTANCE_MAX){//注意这点,需要重新检查,因为可能有多个线程(超过3个)等待锁,但是只有前3个线程能建立对象
ThreeSingle threeSingle = new ThreeSingle();
list.add(threeSingle);
System.out.println("add");
}
}
}
// System.out.println(list.size());
return list.get(new Random().nextInt(list.size()));
}
}
测试方法
public static void main(String[] args) {
for (int i =0;i<100;i++){
new Thread(()->{
ThreeSingle.getInstance();
}).start();
}
}
得到的结果为:
list size为0
add
list size为1
add
list size为2
add
list size为3
list size为3
list size为3
list size为3
list size为3
结果中有多个 list size为3 ,说明在有超过3个线程在等待锁,获取锁之后,可能 list 已经超过3个了,所以这里需要再次检查。这就是为什么需要双检查的原因。
如果你的多线程的知识储备不够,建议可以看看笔者的另外一篇博文:https://blog.csdn.net/xiaoluo5238/article/details/104380207