1生产者 消费者模式
1.1 生产者和消费者模式概述
生产者和消费者是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。
所谓生产者消费者的问题,实际上主要包含了两类线程:
一类:是生产者线程用于生产数据。
一类:是消费者线程用于消费数据。
为了解耦生产者和消费者的关系,通常会采用共享数据区域,就像一个仓库。
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者行为。
消费者只需要从共享数据区中去获取数据,并不要关心生产者的行为。
1.2 Object类的等待和唤醒方法
Void wait() 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll() 方法
Void notify() 唤醒正在等待对象监视器的单个线程。
Void notifyAll() 唤醒正在等待对象监视器的所有线程。
1.3 生产者和消费者案例(基础)
(1)案件(一):
public class Box {
private int milk;
private boolean state =false;
//生产牛奶
public synchronized void put(int milk){
//表示有牛奶,等待消费。
if(state) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//表示没有牛奶,就生产牛奶。
this.milk = milk;
System.out.println("送奶工将第" + this.milk + "瓶牛奶放入奶箱");
//生产完毕之后,修改奶箱状态
state =true;
notifyAll();
}
public synchronized void get(){
//如果没有就等待生产
if(!state) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有牛奶就消费
System.out.println("用户拿到第" + this.milk + "瓶奶");
//修改奶箱状态,表示没有牛奶了
state =false;
notifyAll();
}
}
public class Customer implements Runnable{
private Box b;
public Customer(Box b) {
this.b=b;
}
@Override
public void run() {
while(true){
b.get();
}
}
}
public class Producer implements Runnable{
private Box b;
public Producer(Box b) {
this.b=b;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
b.put(i);
}
}
}
public class Test {
public static void main(String[] args) {
Box b =new Box();
Producer p =new Producer(b);
Customer c = new Customer(b);
Thread t1 =new Thread(p);
Thread t2 =new Thread(c);
t1.start();
t2.start();
}
}
ps:wait和notify方法使用必须要有锁对象,故应该在同步方法里面使用
(2)案件(二):
public class Customer extends Thread {
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.cake == 0) {
break;
} else {
if (!Desk.state) {
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("顾客正在吃蛋糕");
Desk.state = false;
Desk.lock.notifyAll();
}
}
}
}
}
public class Producer extends Thread {
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.cake == 0) {
break;
} else {
if (Desk.state) {
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("厨师正在生产汉堡");
Desk.cake--;
Desk.state = true;
Desk.lock.notifyAll();
}
}
}
}
}
public class Desk {
public static int cake=10;
public static boolean state =false;
public static final Object lock =new Object();
}
public class Test {
public static void main(String[] args) {
Customer c = new Customer();
Producer p = new Producer();
c.start();
p.start();
}
}
2 阻塞队列基本应用
2.1阻塞队列的基本使用
☆ 阻塞队列继承结构:
☆常见BlockingQueue的实例类:
ArrayBlockingQueue:底层是数组,有界
LinkedBlockingQueue:底层是链表,*,但不是真正的*,最大为int的 最大值
ps:每一层的继承都是有用的,每一层都有自己独特的方法可以用
☆BlockingQueue的核心方法
put(Object):将参数放入队列,如果放不去会阻塞。
take():取出第一个数据,取不到也会阻塞。
public class A {
public static void main(String[] args) throws InterruptedException {
//创建一个阻塞队列的对象,容量为1.
ArrayBlockingQueue<String> s=new ArrayBlockingQueue<String>(1);
s.put("蛋糕");
System.out.println(s.take());
System.out.println(s.take());
System.out.println("程序结束");
}
}
程序都取完数据了,再取造成了阻塞
☆利用阻塞队列实现等待唤醒机制
public class Cooker extends Thread{
private ArrayBlockingQueue<String> bd;
public Cooker(ArrayBlockingQueue<String> bd) {
this.bd=bd;
}
@Override
public void run() {
while(true) {
try {
bd.put("蛋糕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Foodie extends Thread{
private ArrayBlockingQueue<String> bd;
public Foodie(ArrayBlockingQueue<String> bd){
this.bd=bd;
}
@Override
public void run() {
while(true){
try {
String take = bd.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
ArrayBlockingQueue<String> bd =new ArrayBlockingQueue<>(2);
Cooker c =new Cooker(bd);
Foodie f =new Foodie(bd);
c.start();
f.start();
}
}
ps:此代码运行下去会一直循环输出蛋糕,输出次数和阻塞队列容量无关。