java
和 c
分别实现下消费者 生产者模式,更好的理解如何通过互斥锁
和条件变量
来保证资源同步
java
package com.toc;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
public class MutexCond {
private static LinkedList<Integer> list = new LinkedList<Integer>();
public static void main(String[] args) throws InterruptedException {
Thread pThread = new Thread(new Producer(list));
Thread cThread = new Thread(new Consumer(list));
pThread.start();
cThread.start();
pThread.join();
cThread.join();
}
public static class Producer implements Runnable{
private LinkedList<Integer> pList;
public Producer(LinkedList<Integer> list) {
this.pList = list;
}
int capacity = 10 ;
int value = 0 ;
@Override
public void run() {
while(true){
// 上锁的对象pList
synchronized (pList) {
if (pList.size() == capacity) {
try {
// 就要由上锁的对象wait,从而释放锁,如果是this,可以省略不写
// 容易出错
pList.wait();
} catch (InterruptedException e) {
}
}
System.out.println("Producer produced + " + value);
value ++ ;
pList.add(value);
// 上锁对象notify
pList.notify();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
}
}
}
}
public static class Consumer implements Runnable{
private LinkedList<Integer> cList;
public Consumer(LinkedList<Integer> list) {
this.cList = list;
}
@Override
public void run() {
while(true){
synchronized (cList) {
if(list.size() == 0){
try {
cList.wait();
} catch (InterruptedException e) {
}
}
int val = cList.removeFirst();
System.out.println("Consumer consumed ----- " + val);
cList.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
}
}
c
// 生产者-消费者的例子,生产者生产一个结构体串在链表的表头上,消费者从表头取走结构体。
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
struct msg {
struct msg *next;
int num;
};
struct msg *head;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *consumer(void *p)
{
struct msg *mp;
for (;;) {
pthread_mutex_lock(&lock);
while (head == NULL)
pthread_cond_wait(&has_product, &lock);
mp = head;
head = mp->next;
pthread_mutex_unlock(&lock);
printf("Consume %d\n", mp->num);
free(mp);
sleep(rand() % 5);
}
}
void *producer(void *p)
{
struct msg *mp;
for (;;) {
mp = malloc(sizeof(struct msg));
pthread_mutex_lock(&lock);
mp->next = head;
mp->num = rand() % 1000;
head = mp;
printf("Produce %d\n", mp->num);
pthread_mutex_unlock(&lock);
pthread_cond_signal(&has_product);
sleep(rand() % 5);
}
}
int main(int argc, char *argv[])
{
pthread_t pid, cid;
srand(time(NULL));
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
总结
c
c在linux下是通过引入互斥锁
(Mutex,Mutual Exclusive Lock)
来实现线程之间的同步的。获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其它线程,没有获得锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。Mutex用pthread_mutex_t类型的变量表示,可以这样初始化和销毁:
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
// 如果Mutex变量是静态分配的(全局变量或static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于用pthread_mutex_init初始化并且attr参数为NULL
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
返回值:成功返回0,失败返回错误号。
线程间的同步还有这样一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。在pthread库中通过条件变量
(Condition Variable)
来阻塞等待一个条件,或者唤醒等待这个条件的线程。Condition Variable
用pthread_cond_t
类型的变量表示,可以这样初始化和销毁:
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
java
在java中
synchronized
其实也是一种互斥锁,而配合wait
和notify
notifyAll
方法,同样实现了条件变量,实现了线程的同步。有一点需要注意的是,加锁的对象一定要明确,给谁加的锁,必须由谁来
wait
和notify
,否则会抛出监视对象不一致的异常。当加锁对象是this时,wait和notify可以省略,但是一定要明确给谁加的。可以改动上面的例子,试着抛出monitor
不一致的异常。