您的位置:首页 > 编程语言 > Java开发

java 多线程学习之多生产者多消费者产生的线程安全问题分析与解决:Lock和Condition

2017-03-15 15:36 1076 查看
//多生产者多消费者
//这是一段会产生错误数据的示例

class Resource {
private String name;
int count;
boolean flag = false;

public synchronized void produce(String name) {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name + count;
++count;
System.out.println(Thread.currentThread().getName()+"----" + name+count + "---"+" produced");
flag = true;
notify();
}

public synchronized void consume() {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + name +count+ "consumed");
flag = false;
notify();
}
}

class Producer implements Runnable {
Resource r;

public Producer(Resource r) {
this.r = r;
}

@Override
public void run() {
while (true) {
r.produce("烤鸭");
}

}
}

class Consumer implements Runnable {
Resource r;

public Consumer(Resource r) {
this.r = r;
}

@Override
public void run() {
while(true){
r.consume();
}
}
}

public class ProducerConsumer {
public static void main(String[] args) {
Resource r=new Resource();
Producer producer=new Producer(r);
Consumer consumer=new Consumer(r);
Thread t0=new Thread(producer);
Thread t1=new Thread(producer);
Thread t2=new Thread(producer);
Thread t3=new Thread(consumer);
Thread t4=new Thread(consumer);
Thread t5=new Thread(consumer);

t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();


1.线程安全问题产生的原因:

当有一个生产线程开始执行的同时,有另外的生产线程和消费线程在被wait时,生产线程在run方法末尾随机唤醒一个进程,恰好唤醒了另一个生产线程,导致该生产线程在flag=true的情况下仍然可以生产

解决办法1:

将flag的if判断变成while循环判断,解决了线程获取执行权后是否应该执行的问题。

将notify改成notifyAll,如果本方唤醒了本方的线程,没有意义,而且while判断+notify会导致死锁。

但是这种方法开销很大,可能造成很多次无用的判断,降低效率

解决办法2:使用Lock和Condition

JDK1.5 以后将同步和锁封装成了对象,并将操作锁的隐式方法变成了显式的动作。

//显式锁的使用示例
Lock  lock =new ReentrantLock();

lock.lock();
try(){
.....
需要同步的代码块
....
}
finally{
lock.unlock();
}


condition接口

子类对象可以由lock的方法获得,一个lock可以有多个condition对象。

1. await();

2. signal();

3. signalAll();

//示例
Lock lock=new ReentrantLock();
Condition cond=lock.newCondition();


使用lock和condition解决多生产者多消费者问题

package MultiThread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* Created by lenovo on 2017/3/14.
*/
class Item{
public int value;

public Item(int value) {
this.value = value;
}
}
class Buffer {
Lock lock = new ReentrantLock();

Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();

final int Max = 100;
int putIndex=0;
int takeIndex=0;
int count = 0;
final Item[] items = new Item[Max];

public void produce() {
lock.lock();
try {
while (count == Max) {
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Item x=new Item(putIndex);
items[putIndex] = x;
System.out.println(x.value+"has been produced");
if (++putIndex == Max)
putIndex = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}

public Item consume() {
lock.lock();
try {
while (count == 0) {
try {
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Item x = items[takeIndex];
System.out.println(x.value+"has been consumed");
if (++takeIndex == Max)
takeIndex = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}

class Producer implements Runnable {
Buffer r;

public Producer(Buffer r) {
this.r = r;
}

@Override
public void run() {
while (true) {
r.produce();
}

}
}

class Consumer implements Runnable {
Buffer r;

public Consumer(Buffer r) {
this.r = r;
}

@Override
public void run() {
while (true) {
r.consume();
}
}
}

public class ProducerConsumer {
public static void main(String[] args) {
Buffer r = new Buffer();
Producer producer = new Producer(r);
Consumer consumer = new Consumer(r);
Thread t0 = new Thread(producer);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(producer);
Thread t3 = new Thread(consumer);
Thread t4 = new Thread(consumer);
Thread t5 = new Thread(consumer);

t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}


**注意点:

1. lock.lock()和lock.unlcok()包围同步代码块

2.lock.unlock() 需要写在finally中,保证即使同步代码块中发生异常,unlock()仍会被执行。

3. 只需唤醒另一个监视器中的一个进程即可,不必signalAll()。**
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐