下面用代码简单实现生产者消费者模型:主要是用的是线程、线程锁、线程条件变量。
先使用c++的代码实现:
#include <jni.h>
#include <string>
#include "pthread.h"
#include "AndroidLog.h"
pthread_t thread;
void *normalCallBack(void * data)
{
LOGD("create normal thread from C++!");
pthread_exit(&thread);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_ywl5320_jnithread_ThreadDemo_normalThread(JNIEnv *env, jobject instance) {
// TODO
pthread_create(&thread, NULL, normalCallBack, NULL);
}
#include "queue"
#include "unistd.h"
pthread_t produc;
pthread_t custom;
pthread_mutex_t mutex;
pthread_cond_t cond;
std::queue<int> queue;
void *producCallback(void *data)
{
while (1)
{
pthread_mutex_lock(&mutex);
queue.push(1);
LOGD("生产者生产一个产品,通知消费者消费, 产品数量为 %d", queue.size());
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
sleep(5);
}
pthread_exit(&produc);
}
void *customCallback(void *data)
{
while (1)
{
pthread_mutex_lock(&mutex);
if(queue.size() > 0)
{
queue.pop();
LOGD("消费者消费产品,产品数量还剩余 %d ", queue.size());
} else{
LOGD("没有产品可以消费, 等待中...");
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
usleep(500 * 1000);
}
pthread_exit(&custom);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_ywl5320_jnithread_ThreadDemo_mutexThread(JNIEnv *env, jobject instance) {
for(int i = 0; i < 10; i++)
{
queue.push(1);
}
// TODO
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_create(&produc, NULL, producCallback, NULL);
pthread_create(&custom, NULL, customCallback, NULL);
}
#include "JavaListener.h"
JavaVM *jvm;
JavaListener *javaListener;
pthread_t chidlThread;
void *childCallback(void *data)
{
JavaListener *javaListener1 = (JavaListener *) data;
javaListener1->onError(0, 101, "c++ call java meid from child thread!");
pthread_exit(&chidlThread);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_ywl5320_jnithread_ThreadDemo_callbackFromC(JNIEnv *env, jobject instance) {
// TODO
javaListener = new JavaListener(jvm, env, env->NewGlobalRef(instance));
//javaListener->onError(1, 100, "c++ call java meid from main thread!");
pthread_create(&chidlThread, NULL, childCallback, javaListener);
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void* reserved)
{
JNIEnv *env;
jvm = vm;
if(vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
{
return -1;
}
return JNI_VERSION_1_6;
}
再用Java实现,逻辑都是一样的,只是语法会有一些不同。
更高并发性能的Lock && Condition
实现三有一个问题,通过实践可以发现,实现二,三的效率明显低于实现一,并发瓶颈很明显,因为在锁 BUFFER_LOCK
看来,任何消费者线程与生产者线程都是一样的。换句话说,同一时刻,最多只允许有一个线程(生产者或消费者,二选一)操作缓冲区 buffer。
而实际上,如果缓冲区是一个队列的话,“生产者将产品入队”与“消费者将产品出队”两个操作之间没有同步关系,可以在队首出队的同时,在队尾入队。理想性能可提升至两倍。
去掉这个瓶颈
那么思路就简单了:需要两个锁 CONSUME_LOCK
与PRODUCE_LOCK
,CONSUME_LOCK
控制消费者线程并发出队,PRODUCE_LOCK
控制生产者线程并发入队;相应需要两个条件变量NOT_EMPTY
与NOT_FULL
,NOT_EMPTY
负责控制消费者线程的状态(阻塞、运行),NOT_FULL
负责控制生产者线程的状态(阻塞、运行)。以此让优化消费者与消费者(或生产者与生产者)之间是串行的;消费者与生产者之间是并行的。
public class LockConditionPreferModel implements Model {
private final Lock CONSUMER_LOCK = new ReentrantLock();
private final Condition NOT_EMPTY_CONDITION = CONSUMER_LOCK.newCondition();
private final Lock PRODUCER_LOCK = new ReentrantLock();
private final Condition NOT_FULL_CONDITION = PRODUCER_LOCK.newCondition();
private AtomicInteger bufLen = new AtomicInteger(0);
private final Buffer<Task> buffer = new Buffer<>();
private final int capacity;
public LockConditionPreferModel(int capacity) {
this.capacity = capacity;
}
private final AtomicInteger taskNo = new AtomicInteger(0);
@Override
public Runnable newRunnableConsumer() {
return new AbstractConsumer() {
@Override
public void consume() throws InterruptedException {
int newBufSize;
CONSUMER_LOCK.lockInterruptibly();
try {
while (bufLen.get() == 0) {
System.out.println("buffer is empty...");
NOT_EMPTY_CONDITION.await();
}
Task task = buffer.poll();
newBufSize = bufLen.decrementAndGet();
assert task != null;
TimeUnit.MILLISECONDS.sleep(500 + (long) (Math.random() * 500));
System.out.println("consume: " + task.getNo());
if (newBufSize > 0) {
NOT_EMPTY_CONDITION.signalAll();
}
} finally {
CONSUMER_LOCK.unlock();
}
if (newBufSize < capacity) {
PRODUCER_LOCK.lockInterruptibly();
try {
NOT_FULL_CONDITION.signalAll();
} finally {
PRODUCER_LOCK.unlock();
}
}
}
};
}
@Override
public Runnable newRunnableProducer() {
return new AbstractProducer() {
@Override
public void produce() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 1000));
int newBufSize;
PRODUCER_LOCK.lockInterruptibly();
try {
while (bufLen.get() == capacity) {
System.out.println("buffer is full...");
NOT_FULL_CONDITION.await();
}
Task task = new Task(taskNo.getAndIncrement());
buffer.offer(task);
newBufSize = bufLen.incrementAndGet();
System.out.println("produce: " + task.getNo());
NOT_FULL_CONDITION.signalAll();
} finally {
PRODUCER_LOCK.unlock();
}
if (newBufSize > 0) {
CONSUMER_LOCK.unlock();
try {
NOT_EMPTY_CONDITION.signalAll();
} finally {
CONSUMER_LOCK.unlock();
}
}
}
};
}
private static class Buffer<E> {
private Node head;
private Node tail;
Buffer() {
head = tail = new Node(null);
}
private void offer(E e) {
tail.next = new Node(e);
tail = tail.next;
}
private E poll() {
head = head.next;
E e = head.item;
head.item = null;
return e;
}
private class Node {
E item;
Node next;
Node(E item) {
this.item = item;
}
}
}
public static void main(String[] args) {
Model model = new BlockingQueueModel(3);
Arrays.asList(1, 2).forEach(x -> new Thread(model.newRunnableConsumer()).start());
Arrays.asList(1, 2, 3, 4, 5).forEach(x -> new Thread(model.newRunnableProducer()).start());
}
}