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
不一致的異常。