Java 多线程编程 生产者 消费者模式
synchronized和 ReentrantLock的相同点
- 两种锁都是独占锁, 只允许线程互斥的访问临界区
- 两种锁都可以重入, 在一个线程可以重复获取多次锁
两者区别是 synchronized重入不用考虑解锁相关问题, 而 ReentrantLock必须确保重复获取锁的次数和释放锁的次数相同, 否者相关锁无法解
synchronized和 ReentrantLock的区别
- synchronized是加解锁是隐式的, ReentrantLock是手动加解锁
- synchronized是通过 Object的 wait和 notify方法来实现线程间的等待通知机制, 但是 ReentrantLock是通过 Condition接口来实现了等待通知机制
- ReentrantLock支持响应中断, 出现死锁现象可以通过 lockInterruptibly()方法响应中断. 但是 synchronized出现死锁时会无限等待,
- ReentrantLock支持公平锁, 被锁后等了最长时间的线程将会优先解锁, 而默认是非公平锁就是随机的 synchronized只支持非公平锁
- 非公平锁性能优于公平锁, 但是公平锁能防止线程饥饿现象发生. 创建方式: new ReentrantLock(true) 参数 true是创建公平锁, false或无参是非公平锁
1. 消息类
- 消息数超过存放最大数, 则生产者线程阻塞(等待消费者消费后唤醒)
- 消息数等于空, 则消费者线程阻塞(等待生产者生产后唤醒)
public class Message {
/** 当前消息数量*/
private int count = 0;
/** 信息存放最大限数*/
private int maximum = 20;
/** 重入锁*/
private Lock lock;
/** 生产者锁控制器*/
private Condition producerCondition;
/** 消费者锁控制器*/
private Condition consumerCondition;
public Message() {}
public Message(final Lock lock, final Condition producerCondition, final Condition consumerCondition) {
this.lock = lock;
this.producerCondition = producerCondition;
this.consumerCondition = consumerCondition;
}
/**
* 生产消息
* */
public void set() {
/** 获取锁*/
lock.lock();
try {
if (count <= maximum) {
/** 生产一个消息*/
System.out.println("生产者 线程" + Thread.currentThread().getName() + "生产了一个消息, 当前有" + (++count) + "个消息");
/** 唤醒等待的消费者*/
consumerCondition.signal();
} else {
try {
/**
* 如果当前消息大于 maximum信息最大数
* 生产者进入睡眠/等待状态
* */
producerCondition.await();
System.out.println("生产者 线程" + Thread.currentThread().getName() + "进入睡眠");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
/** 释放锁*/
lock.unlock();
}
}
/**
* 消费消息
* */
public void get() {
/** 获取锁*/
lock.lock();
try {
if (count > 0) {
/** 消费一个消息*/
System.out.println("消费者 线程" + Thread.currentThread().getName() + "消费了一个消息, 当前有" + (--count) + "个消息");
/** 唤醒等待的生产者*/
producerCondition.signal();
} else {
try {
/** 如果没有消息, 消费者进入睡眠/等待状态*/
consumerCondition.await();
System.out.println("消费者 线程" + Thread.currentThread().getName() + "进入睡眠");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
/** 释放锁*/
lock.unlock();
}
}
}
2. 生产者类
public class Producer implements Runnable {
private Message message;
public Producer(Message message) {
this.message = message;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
message.set();
}
}
}
3. 消费者类
public class Consumer implements Runnable {
private Message message;
public Consumer(Message message) {
this.message = message;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
message.get();
}
}
}
Runnable是接口, Thread是 Runnable的实现类, 由于 Java对多接口实现比较灵活, 开发多线程时建议实现 Runnable接口. 启动时用 Thread的 start(). 例: new Thread().start()
4. App.java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class App {
public static void main(String[] args) {
/** 重入锁*/
final Lock lock = new ReentrantLock();
/** 生产者锁控制器*/
final Condition producerCondition = lock.newCondition();
/** 消费者锁控制器*/
final Condition consumerCondition = lock.newCondition();
final Message message = new Message(lock, producerCondition, consumerCondition);
/** 建几个生产线程*/
new Thread(new Producer(message)).start();
new Thread(new Producer(message)).start();
new Thread(new Producer(message)).start();
/** 建几个消费线程*/
new Thread(new Consumer(message)).start();
new Thread(new Consumer(message)).start();
new Thread(new Consumer(message)).start();
new Thread(new Consumer(message)).start();
}
}
如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!