-
前言
-
正文
- 方案一
- 方案二
- 方案三
- 方案四
- 方案五
-
结尾
前言
自己刚刚实习的时候,选择的是广电行业的音视频开发岗。当时每个实习生,公司都会安排一个经验丰富的实习导师。平时的工作内容都是自己的实习导师进行安排和验收。
正文
有一天,我们开发小组分到了一个开发安卓盒子的任务,具体的任务内容是要求在安卓盒子上开发一个播放器,能够预览本地的视频画面,同时,将盒子采集的音频和视频数据发送出去。
整个播放器的设计架构大致分为三层,分别是上层的界面层和业务层,中间适配层,底层数据处理层。其中,界面层和业务层完全用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