如何用Java实现靠谱的播放器单例模式【奔跑吧!JAVA】

举报
liuzhen007 发表于 2021/06/14 15:07:13 2021/06/14
【摘要】 前言正文方案一方案二方案三方案四结尾 前言自己刚刚实习的时候,选择的是广电行业的音视频开发岗。当时每个实习生,公司都会安排一个经验丰富的实习导师。平时的工作内容都是自己的实习导师进行安排和验收。 正文有一天,我们开发小组分到了一个开发安卓盒子的任务,具体的任务内容是要求在安卓盒子上开发一个播放器,能够预览本地的视频画面,同时,将盒子采集的音频和视频数据发送出去。整个播放器的设计架构大致分为三...

image.png

  • 前言

  • 正文

    1. 方案一
    2. 方案二
    3. 方案三
    4. 方案四
    5. 方案五
  • 结尾

前言

自己刚刚实习的时候,选择的是广电行业的音视频开发岗。当时每个实习生,公司都会安排一个经验丰富的实习导师。平时的工作内容都是自己的实习导师进行安排和验收。

正文

有一天,我们开发小组分到了一个开发安卓盒子的任务,具体的任务内容是要求在安卓盒子上开发一个播放器,能够预览本地的视频画面,同时,将盒子采集的音频和视频数据发送出去。

整个播放器的设计架构大致分为三层,分别是上层的界面层和业务层,中间适配层,底层数据处理层。其中,界面层和业务层完全用Java语言开发,完美兼容了安卓系统。适配层是JNI层,熟悉安卓Native开发的小伙伴肯定对JNI非常了解,我们可以把JNI理解成是Java和C++的转换层。数据处理层就是音视频数据的采集层,主要工作有采集、编码、组包、发送等。

播放器具体的架构模型如下图所示:

方案一

具体到自己的任务是在适配层完成数据转换,同时提供SDK,保证播放器初始化模型为单例模式。于是,自己最先想到了饿汉模式,具体实现可以参考如下代码。

实现代码:

public class Player {  
    private static Player instance = new Player();  
    private Player (){}  
    public static Player getInstance() {  
    	return instance;  
    }  
}

饿汉单例模式的优点是效率高,类定义的时候就完成了初始化操作,需要的时候可以直接拿过来用,执行效率较高。但是缺点也是非常明显的,不管三七二十一都会创建单例实例,如果程序用不到的话,可能会造成资源浪费。

方案二

那我们能不能做到按需分配呢?就是使用的时候再初始化单例对象。答案是肯定的,也就是我们经常说的懒汉单例模式。它都有哪些特点呢?我们通过下面的代码来了解一下。

实现代码:

public class Player {  
    private static Player instance;  
    private Player (){}  
  
    public static Player getInstance() {  
        if (instance == null) {  
            instance = new Player();  
        }  
        return instance;  
    }  
}

上面的代码基本上就实现了一个简单的懒汉单例模式类,乍一看,是没有问题的。但是稍微多想一点,我们就会发现其中的问题,这个单例类不是线程安全的。当程序存在多线程调用时,就会出现问题。

方案三

上面的单例模式方法存在多线程问题,我们有没有什么改进方法能够让上面的方法支持多线程调用呢?答案是可以的。我们可以通过加锁机制来保证多线程安全,接下来,我们通过代码看一下具体的实现方式。

实现代码:

public class Player {  
    private static Player instance;  
    private Player (){}  
    public static synchronized Player getInstance() {  
   	    if (instance == null) {  
		    instance = new Player();  
        }
        return instance;  
    }  
}

通过上述代码,我们可以发现,懒汉模式是在第一次调用的时候才初始化对象实例,避免了内存浪费。缺点也非常明显,就是第一次调用的时候会比较慢,因为是临时创建的,但是之后就和饿汉模式一样了。

方案四

分析之后,我发现方案三确实可以满足多线程调用的场景,但是效率会比较低。有没有更好的方案呢?在保证线程安全的同时,也有较高的执行效率。是的,确实存在这种方案,就是我们马上要介绍的双重校验锁方式。

实现代码:

public class Player {  
    private volatile static Player singleton;  
    private Player (){}  
    public static Player getSingleton() {  
        if (singleton == null) {  
            synchronized (Player.class) {  
                if (singleton == null) {  
                    singleton = new Player();  
                }  
            }  
        }  
        return singleton;  
    }  
}

这种方式能够保证getSingleton()方法具备较高的执行效率,不会像方案三那样重复调用时会被阻塞。

方案五

其实,还有一种比较常见的单例设计模式,就是静态类方式。这种方式实现更加简单,也能实现双重校验锁方式的效果,但使用场景比较受限,当需要延迟初始化时才适用。

实现代码:

public class Player {  
    private static class Singleton {  
    	private static final Player INSTANCE = new Player();  
    }  
    private Player (){}  
    public static final Player getInstance() {  
    	return Singleton.INSTANCE;  
    }  
}

很明显,这种方式是不适合我们播放器的开发需求的。这里就不过多介绍了,感兴趣的小伙伴,可以自行查阅资料补充。

结尾

由于我们使用场景的特点,在播放器实例创建后,基本上一定会被使用到,同时考虑到播放器需要支持快速预览等功能,我们最终采用了饿汉单例模式进行播放器SDK的实现框架模型。

【奔跑吧!JAVA】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/265241

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。