ReentrantLock 和 Conditicon 做一个生产者-消费者模型

87 阅读3分钟

背景

学习 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();

    }
}

反思

  • FlowControlEncoderTaskDecoderTask 设计成依赖关系,会更好!