生產者消費者問題是一個著名的線程同步問題,該問題描述如下:有一個生產者在生產產品,這些產品將提供給若干個消費者去消費,爲了使生產者和消費者能併發執行,在兩者之間設置一個具有多個緩衝區的緩衝池,生產者將它生產的產品放入一個緩衝區中,消費者可以從緩衝區中取走產品進行消費,顯然生產者和消費者之間必須保持同步,即不允許消費者到一個空的緩衝區中取產品,也不允許生產者向一個已經放入產品的緩衝區中再次投放產品。
使用synchronized關鍵字實現線程同步
在使用wait()和notifyAll()方法時,應注意將wait()方法放入循環中,否則會產生虛假喚醒問題。
/**
* Created by 吳海飛 on 2017-1-23.
*/
public class TestProductAndConsumer {
public static void main(String[] args){
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(pro,"生產者A").start();
new Thread(consumer,"消費者B").start();
}
}
/**
* 店員,可以進貨與銷售貨物
*/
class Clerk{
private int product = 0;
/**
* 進貨的方法
*/
public synchronized void get(){
while (product>=1){//爲了避免虛假喚醒問題,應該總是使用在循環中
System.out.println("產品已滿!");
try {
this.wait();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":" + ++product);
this.notifyAll();//喚醒線程
}
/**
* 銷售的方法
*/
public synchronized void sale(){
while (product<=0){//爲避免虛假喚醒,應該總是始終使用在循環中
System.out.println("缺貨……");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":"+ --product);
this.notifyAll();
}
}
/**
* 生產者
*/
class Productor implements Runnable{
private Clerk clerk;
public Productor(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 10; i++){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
/**
* 消費者
*/
class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 10; i++){
clerk.sale();
}
}
}
使用同步鎖實現線程同步問題
使用同步鎖時應注意lock()與unlock()方法的同步使用。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用ReentrantLock實現生產者消費者問題
* Created by 吳海飛 on 2017-1-23.
*/
public class TestReentrantLock {
public static void main(String[] args){
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(pro,"生產者A").start();
new Thread(consumer,"消費者B").start();
new Thread(pro,"生產者C").start();
new Thread(consumer,"消費者D").start();
}
}
class Clerk{
private Lock lock = new ReentrantLock();//獲取同步鎖
private Condition condition = lock.newCondition();
private int product = 0;
/**
* 進貨的方法
*/
public void get(){
lock.lock();//打開鎖
try{
while (product>=1){//爲了避免虛假喚醒問題,應該總是使用在循環中
System.out.println("產品已滿!");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":" + ++product);
condition.signalAll();
}finally {
lock.unlock();//關閉鎖
}
}
/**
* 銷售的方法
*/
public void sale(){
lock.lock();//加鎖
try {
while (product<=0){//爲避免虛假喚醒,應該總是始終使用在循環中
System.out.println("缺貨……");
try {
condition.await();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":"+ --product);
condition.signalAll();//喚醒等待
}finally {
lock.unlock();//釋放鎖
}
}
}
/**
* 生產者
*/
class Productor implements Runnable{
private Clerk clerk;
public Productor(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 10; i++){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
/**
* 消費者
*/
class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 10; i++){
clerk.sale();
}
}
}