背景
学习 android MediaCodec 的编解码,在Github 上代码在 编码,从队列获取数据的时候,出现的crash,所有就研究一下,,发现ta 用了生产者-消费者模型,有点想法,自己若是用 ReentrantLock 来实现,能不能也做出来呢?
- 那就开吧....
代码设计失败的地方
- 使用了 继承,没有使用 线程操作资源类的 思想;
- 导致 生产者 和 消费者 持有的同一把锁lock ,变得十分费劲;
- 线程 之间,需要处理 volatile 相关变量的可见性 比较多;
- 生产者 要 停止的时候, signalAll() , 无法使用try{} finally {} 写法'
相关代码
- 控制并发流 : FlowControl
public abstract class FlowControl {
private static final String TAG = "FlowControl : ";
//private volatile LinkedList<byte[]> byteQueue;
private static final int QUEUE_LIME_SIZE = 3;
public FlowControl() {
}
abstract Condition getConsumerCondition();
abstract Condition getProducerCondition();
abstract ReentrantLock getReentrantLock();
/**
* 数据
*
* @return
*/
abstract LinkedList<byte[]> getCommonQueue();
public byte[] takeByte() {
byte[] result = null;
getReentrantLock().lock();
try {
if (getCommonQueue().isEmpty()) {
if (TaskManager.mIsDecoderOver) {
Log.e(TAG, " takeByte : mIsDecoderOver is TRUE:" + getCommonQueue().size());
return null;
}
Log.e(TAG, " takeByte : 队列空的 编码线程 在等待,当前 队列大小:" + getCommonQueue().size());
getConsumerCondition().await();
}
if (getCommonQueue().peekFirst() != null) {
result = getCommonQueue().removeFirst();
}
getProducerCondition().signalAll();
Log.e(TAG, " takeByte : tell解码线程,可以工作,当前 队列大小:" + getCommonQueue().size());
return result;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
getReentrantLock().unlock();
}
return null;
}
public void putByte(byte[] data) {
Log.d(TAG, "putByte : thread is : " + Thread.currentThread().getName());
getReentrantLock().lock();
try {
if (getCommonQueue().size() >= QUEUE_LIME_SIZE) {
Log.e(TAG, " putByte : 队列满了,解码线程在等待,当前 队列大小:" + getCommonQueue().size());
getProducerCondition().await();
}
getCommonQueue().addLast(data);
Log.e(TAG, " putByte : 往队列中添加了一条数据,当前 队列大小:" + getCommonQueue().size());
Log.e(TAG, " putByte : 📢📢📢📢通 知编码线程,可以工作了 ,当前 队列大小:" + getCommonQueue().size());
getConsumerCondition().signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
getReentrantLock().unlock();
}
}
}
- 生产者 : DecoderTask
public class DecoderTask extends FlowControl {
LinkedList<byte[]> queue;
private volatile ReentrantLock lock = null;
private volatile Condition consumer = null;
private volatile Condition producer = null;
public DecoderTask(LinkedList<byte[]> queue, ReentrantLock lock, Condition consumer, Condition producer, boolean mIsDecoderOver) {
this.queue = queue;
this.lock = lock;
this.consumer = consumer;
this.producer = producer;
}
@Override
Condition getConsumerCondition() {
return consumer;
}
@Override
Condition getProducerCondition() {
return producer;
}
@Override
ReentrantLock getReentrantLock() {
return lock;
}
@Override
LinkedList<byte[]> getCommonQueue() {
return queue;
}
int count = 0;
private static final String TAG = "DecoderTask : ";
public void startDecoderTask() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
if (count == 3) {
getReentrantLock().lock();
TaskManager.mIsDecoderOver = true;
getConsumerCondition().signalAll();
getReentrantLock().unlock();
Log.e(TAG, " startDecoderTask : 👌👌👌👌解码线程完成工作了. " + getCommonQueue().size());
break;
}
putByte(new byte[2]);
count++;
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
} finally {
// getReentrantLock().unlock();
}
}
}
}, "DecoderTask-thread").start();
}
}
- 消费者:EncoderTask
public class EncoderTask extends FlowControl {
private static final String TAG = "EncoderTask : ";
LinkedList<byte[]> queue;
private volatile ReentrantLock lock = null;
private volatile Condition consumer = null;
private volatile Condition producer = null;
public EncoderTask(LinkedList<byte[]> queue, ReentrantLock lock, Condition consumer, Condition producer, boolean isDecoderOver) {
this.queue = queue;
this.lock = lock;
this.consumer = consumer;
this.producer = producer;
}
@Override
Condition getConsumerCondition() {
return consumer;
}
@Override
Condition getProducerCondition() {
return producer;
}
@Override
ReentrantLock getReentrantLock() {
return lock;
}
@Override
LinkedList<byte[]> getCommonQueue() {
return queue;
}
public void startEncoderTask() {
new Thread(new Runnable() {
@Override
public void run() {
while (true){
if(getCommonQueue().isEmpty() && TaskManager.mIsDecoderOver){
Log.d(TAG, "startEncoderTask: 队列为空了,并且,解码线程也结束了," +
"编码线程已经消耗完所有的数据,所以也可以结束了.");
return;
}
Log.e(TAG, " invoke takeByte " );
byte[] bytes = takeByte();
}
}
}, "EncoderTask-thread").start();
}
}
- log 看代码走向: Log
public class Log {
static void d(String tag, String content) {
System.err.println(tag + content);
}static void e(String tag, String content) {
System.err.println(tag + content);
}
}
- 测试类 TaskManager
public class TaskManager {
private volatile LinkedList<byte[]> queue;
private DecoderTask decoderTask;
private EncoderTask encoderTask;
private volatile ReentrantLock lock = null;
private volatile Condition consumer = null;
private volatile Condition producer = null;
public static volatile boolean mIsDecoderOver = false;
volatile boolean isDecoderOver = false;
public TaskManager() {
this.queue = new LinkedList<>();
lock = new ReentrantLock();
consumer = lock.newCondition();
producer = lock.newCondition();
decoderTask = new DecoderTask(queue, lock, consumer, producer,isDecoderOver);
encoderTask = new EncoderTask(queue, lock, consumer, producer,isDecoderOver);
}
public void startAllTask() {
decoderTask.startDecoderTask();
encoderTask.startEncoderTask();
}
public static void main(String[] args) {
TaskManager taskManager = new TaskManager();
taskManager.startAllTask();
}
}
反思
- FlowControl 和 EncoderTask,DecoderTask 设计成依赖关系,会更好!