生产者消费者模式是一个经典的多线程模式,要搞懂线程的同步异步, 从生产者消费者模式出发是一个很好的学习方式,但是这个模式是面向过程开发的软件模式, 它并不是面向对象的23种JAVA设计模式。
关于生产者消费者模式,就是
- 生产者线程(可以是一个线程生产,也可以是多个线程生产)生成产品放到仓库里
- 消费者线程(可以是一个线程消费,也可以是多个线程消费)从仓库取出产品消耗
虽然他们的功能(生产、消费)不一样,但是处理的数据(生产出的产品)是相同的,这样是牵扯出来一个多线程通讯的问题----------在不同线程间传递数据
有关生产者消费者,有这么几种形式:
- 单生产者单消费者模式
- 多生产者多消费者模式
分别使用synchronize/wait()/notify()/notifyAll()、lock()/unLock()的不同方式去实现,在实现这个模式之前,我们有必要介绍一下线程的五种状态:
- 创建
- 就绪
- 运行
- 阻塞
- 死亡
线程Thread类的等待、唤醒函数
- wait和notify/notifyAll,与其他函数不同的是,这三个函数是定义在Object类中的,这意味着他们是任何类都共有的"属性",wait与notify总是成对出现的(等待,需要后期唤醒它,否则直接杀死就好,要唤醒,那么前提是它处于等待状态,运行状态不用唤也非常清醒),wait函数不是任意情形下都能开始等待的,其调用者需要成为Object的监视器(monitor),wait函数的调用才是有效的,是需要基于这个object的synchronized同步,才能给调用者这个对象加上monitor,而notify是随机唤醒这个对象锁所在线程池中任意的一个睡眠线程(且是不可预知的),notifyAll是唤醒所有(它是可以将所有线程唤醒,但是CPU具体是选择哪个线程也是不可控的,这里是唤醒所有但是运行的时候还是只运行一个线程,不考虑多核CPU移动设备)
- interrupt,它是一种被动的中断状态,有三种状态:①、如果线程正被blocked正在某个obj的wait、join、sleep中,线程会被唤醒,中断状态被清除且收到interrupt异常;②、如果线程正被blocked在interrupttibleChannel的I/O操作,中断会被置位接收CloseByInterruptException异常;③、如果线程正被blocked在selector中,中断置位且马上返回
- join 翻译是节点,也就是说join函数可以控制不同线程的执行顺序,它能保证T1执行完毕才去执行T2.start(),带时间参数的t1.join(long millis),会多一个限制,如果在规定时间内T1没有完成,那么时间到了以后T2也会执行
- sleep 这个就是直接睡着,但是不释放锁,CPU会一直阻塞系统并等待它醒来继续持有CPU执行权,而wait沉睡时,是释放对象锁的
事实上,wait系列的三个函数是有很多缺陷的,睡眠和唤醒与锁完全耦合在一起,导致obj1锁同步代码块中wait状态的线程只能由obj1锁所在线程池notify去唤醒,再一个,在synchronized同步时,锁是隐式且自动的拿到锁,当执行完这一任务后,锁又隐式的自动释放锁,这一行为完全无法人为控制,
从JDK 1.5开始,java提供了java.util.concurrent.locks包,这个包里提供了:①、Lock接口;②、condition接口、ReadWriterLock接口,前两个接口将锁和monitor的方法(wait、notify)解耦了,其中Lock只提供锁,通过锁方法new condition()可以生成一个或多个与该锁有关联的监视器monitor,每个monitor都有自己的wait 、notify方法。也就是说Lock代替了synchronized方法和同步代码块的使用,condition代替了object的monitor的方法使用
当某线程执行condition1.await()时,该线程将进入condition1监视器对应的线程池睡眠,当执行condition1.signal()时,将随机唤醒condition1线程池中的任意一个线程,当执行condition1.signalAll()时,将唤醒condition1线程池中的所有线程。同理,对于condition2监视器也是一样的。即使有多个监视器,但只要它们关联的是同一个锁对象,就可以跨监视器操作对方线程。例如condition1中的线程可以执行condition2.signal()来唤醒condition2线程池中的某个线程。
import java.util.concurrent.locks.*;
Lock l = new ReentrantLock();
Condition con1 = l.newCondition();
condition con2 = l.newCondition();
l.lock();
try{
//包含await()、signal()或signalAll()的代码段...
//con1 中的线程 可以调用 con2.signal(),从而唤醒con2 所在线程池中await的线程
} finally {
l.unlock(); //由于代码段可能异常,但unlock()是必须执行的,所以必须使用try,且将unlock()放进finally段
}
下面来练习单生产者单消费者
一个生产者线程,一个消费者线程,生产者每生产一个产品放进仓库里,消费者从仓库里取出产品包进行消费。其中生产者判断是否继续生产的依据是仓库里没有产品,而消费者判断是否消费的依据是仓库里有产品。由于这个模式中,仓库一直只放一个产品,因此可以把盘子省略掉,生产者和消费者直接手把手地交递产品即可。
首先需要描述这三个类,一是多线程共同操作的资源(此处即产品),二是生产者,三是消费者。在下面的例子中,我把生产产品和消费产品的方法分别封装到了生产者和消费者类中,如果把它们封装在产品类中则更容易理解。
首先用synchronized同步代码块实现
//产品类
package com.my_project.test_more_thread;
/**
* Created by Administrator on 2019\2\25 0025.
* 生产者和消费者先后处理的product资源是同一个,要确保这一点,
* 可以按单例模式来设计product类,也可以将同一个product对象通过构造方法传递给生产者和消费者,
*/
public class Products {
private String name;
private int count = 1;
private boolean flag = false;//为wait和notify提供判断标记
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
//生产者---生产工厂
/**
* Created by Administrator on 2019\2\25 0025.
*/
public class ProductFactory implements Runnable {
private Products p;
public ProductFactory(Products product) {
this.p = product;
}
//对外暴露生产产品的方法
public void startProduct(String name) {
p.setName(name + p.getCount());
p.setCount(p.getCount() + 1);
}
@Override
public void run() {
while (true) {
synchronized (Products.class) {
if (p.isFlag()) {
//没有在消费,说明仓库没有产品了,生产者不需要睡眠,赶紧醒来工作了
try {
Products.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
startProduct("汉堡");
Log.e("ProductFactory", Thread.currentThread().getName() + "----生产者------" + p.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
p.setFlag(true);
Products.class.notify();
}
}
}
}
//消费者---顾客
/**
* Created by Administrator on 2019\2\25 0025.
*/
public class _Customer implements Runnable {
private Products p;
public _Customer(Products product) {
this.p = product;
}
//提供消费产品的方法
public String consume() {
return p.getName();
}
@Override
public void run() {
while (true) {
synchronized (Products.class) {
if (!p.isFlag()) {
try {
Products.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e("_Customer", Thread.currentThread().getName() + "----消费者------" + consume());
try {
Thread.sleep(1000);
} catch (InterruptedException i) {
}
p.setFlag(false);
Products.class.notify();
}
}
}
}
//测试
main(){
Products product = new Products();
ProductFactory pro = new ProductFactory(product);
_Customer con = new _Customer(product);
Thread p = new Thread(pro);
Thread c = new Thread(con);
p.start();
c.start();
}
来看上边代码,显然重点就在线程的run方法中,首先启动两个线程,默认产品的isFlag属性为false,目的是控制生产者和消费者两线程,让消费者线程wait睡眠,而生产者线程生产产品,当生成一个产品,isFlag属性改为true,同时唤醒Products.class这个类对应线程池中沉睡的线程,isFlag属性继续控制线程,这时是生产者线程wait,消费者线程开启,去消费一个产品,然后一直循环下去。
下班用lock锁来实现单生产者单消费者模式:
//产品类
public class Products_1 {
private String name;
private int count = 1;
private boolean flag = false;//为wait和notify提供判断标记
//为生产者和消费者提供同一个锁对象和同一个condition
public Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
}
//生产者
package com.my_project.test_more_thread.product;
import android.util.Log;
import com.my_project.test_more_thread.goods.Products_1;
/**
* Created by Administrator on 2019\2\25 0025.
*/
public class ProductFactory_1 implements Runnable {
private Products_1 p;
public ProductFactory_1(Products_1 product_1) {
this.p = product_1;
}
//对外暴露生产产品的方法
public void startProduct(String name) {
p.setName(name + p.getCount());
p.setCount(p.getCount() + 1);
}
@Override
public void run() {
while (true) {
//使用lock锁
p.lock.lock();
try {
if (p.isFlag()) {
//没有在消费,说明仓库没有产品了,生产者不需要睡眠,赶紧醒来工作了
try {
p.condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
startProduct("汉堡");
Log.e("ProductFactory", Thread.currentThread().getName() + "----生产者------" + p.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
p.setFlag(true);
p.condition.signal();
} catch (Exception e) {
} finally {
p.lock.unlock();
}
}
}
}
//消费者
package com.my_project.test_more_thread.customer;
import android.util.Log;
import com.my_project.test_more_thread.goods.Products_1;
/**
* Created by Administrator on 2019\2\25 0025.
*/
public class _Customer_1 implements Runnable {
private Products_1 p;
public _Customer_1(Products_1 product_1) {
this.p = product_1;
}
//提供消费产品的方法
public String consume() {
return p.getName();
}
@Override
public void run() {
while (true) {
p.lock.lock();
try {
if (!p.isFlag()) {
try {
p.condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e("_Customer", Thread.currentThread().getName() + "----消费者------" + consume());
try {
Thread.sleep(1000);
} catch (InterruptedException i) {
}
p.setFlag(false);
p.condition.signal();
} catch (Exception e) {
} finally {
p.lock.unlock();
}
}
}
}
从代码来看其实与synchronized同步没什么区别,只是finally中必须去解锁,下面再来看看多生产者多消费者模式,但是产品在绝对某个时刻数量还是只有一个的模式,其实这样的模式现实中是没有的,没有哪个流水线会只做一个产品出来,这里只是为了循序渐进,更能清晰的说明是如何从单生产者单消费者模式演变而来的
从单生产者单消费者到多生产者多消费者模式,因为线程安全问题和死锁问题,有两个问题需要考虑:
- 对某一方来说,如何让多线程达到和单线程一样的生产或消费功能?多线程和单线程最大的区别就是线程间的安全问题,只要保证线程间任务执行的同步
- 两方如何协调配合完成生产消费功能,也就是说,当生产时,消费线程睡眠,消费时,生产线程睡眠,只需在某一方执行完同步任务时,唤醒另一方即可。
实质上,多线程就两个问题,同步不同步和死锁。
- 当生产方和消费方都出现了多线程,可以将生产方的多线程看成一个线程整体、消费方的多线程也看成一个整体,这解决的是线程安全问题。
- )再将生产方整体和消费方整体两方结合起来看成多线程,来解决死锁问题,而java中解决死锁的方式就是唤醒对方或唤醒所有。
问题是如何保证某一方的多线程之间同步?以多线程执行单消费方的代码为例进行分析。
synchronized (Products.class) {
if (!p.isFlag()) {
try {
Products.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.e("_Customer", Thread.currentThread().getName() + "----消费者------" + consume());
try {
Thread.sleep(1000);
} catch (InterruptedException i) {
}
p.setFlag(false);
Products.class.notify();
}
}
假设消费线程1消费完一个产品后唤醒了消费线程2,并继续循环,判断if(!flag),它将wait,于是锁被释放。假设CPU正好选中了消费线程2,那么消费线程2也将进入wait。当生产方生产了一个产品后,假设唤醒了消费线程1,它将从wait语句处继续向下消费刚生产完的产品,假设正好再次唤醒了消费线程2,当消费线程2被CPU选中后,消费线程2也将从wait语句处向下消费,消费的也是刚才生产的产品,问题再此出现了,连续唤醒的消费线程1和2消费的是同一个产品,也就是说产品被重复消费了。这又是多线程不同步问题。
说了一大段,其实将视线放大后分析就很简单了,只要某一方的2个或多个线程都因为判断b.flag而wait,那么这两个或多个线程有可能会被连续唤醒而继续向下生产或消费。这造成了多线程不同步问题。
不安全的问题就出在同一方的多个线程在连续唤醒后继续向下生产或消费。这是if语句引起的,如果能够让wait的线程在唤醒后还回头判断b.flag是否为true,就能让其决定是否继续wait还是向下生产或消费。
可以将if语句替换为while语句来满足要求。这样一来,无论某一方的多个线程是否被连续唤醒,它们都将回头判断b.flag。
@Override
public void run() {
while (true) {
synchronized (Products.class) {
while (!p.isFlag()) {
try { Products.class.wait(); } catch (InterruptedException e) {e.printStackTrace();}
}
Log.e("_Customer", Thread.currentThread().getName() + "----消费者------" + consume());
try {Thread.sleep(1000);} catch (InterruptedException i) {}
p.setFlag(false);
Products.class.notify();
}
}
}
解决了第一个多线程安全的问题,但会出现死锁问题。这很容易分析,将生产方看作一个整体,将消费方也看作一个整体,当生产方线程都wait了(生产方的线程被连续唤醒时会出现该方线程全部wait),消费方也都wait了,死锁就出现了。其实放大了看,将生产方、消费方分别看作一个线程,这两个线程组成多线程,当某一方wait后无法唤醒另一方,另一方也一定会wait,于是就死锁了。
对于双方死锁的问题,只要保证能唤醒对方,而非本方连续唤醒就能解决。使用notifyAll()或signalAll()即可,也可以通过signal()唤醒对方线程解决,见下面的第二段代码。
根据上面的分析,将单生产、单消费模式的代码改进一下,就可以变为多生产多消费单产品模式。
//产品
public class Products {
private String name;
private int count = 1;
private boolean flag = false;//为wait和notify提供判断标记
}
//生产者
package com.my_project.test_more_thread.product;
import android.util.Log;
import com.my_project.test_more_thread.goods.Products;
/**
* Created by Administrator on 2019\2\25 0025.
*/
public class ProductFactory implements Runnable {
private Products p;
public ProductFactory(Products product) {
this.p = product;
}
//对外暴露生产产品的方法
public void startProduct(String name) {
p.setName(name + p.getCount());
p.setCount(p.getCount() + 1);
}
@Override
public void run() {
while (true) {
synchronized (Products.class) {
while (p.isFlag()){
//没有在消费,说明仓库没有产品了,生产者不需要睡眠,赶紧醒来工作了
try {Products.class.wait();} catch (InterruptedException e) {e.printStackTrace();}
}
startProduct("汉堡");
Log.e("ProductFactory", Thread.currentThread().getName() + "----生产者------" + p.getName());
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
p.setFlag(true);
Products.class.notifyAll();
}
}
}
}
//消费者
package com.my_project.test_more_thread.customer;
import android.util.Log;
import com.my_project.test_more_thread.goods.Products;
/**
* Created by Administrator on 2019\2\25 0025.
*/
public class _Customer implements Runnable {
private Products p;
public _Customer(Products product) {
this.p = product;
}
//提供消费产品的方法
public String consume() {
return p.getName();
}
@Override
public void run() {
while (true) {
synchronized (Products.class) {
while (!p.isFlag()) {
try {Products.class.wait(); } catch (InterruptedException e) {e.printStackTrace();}
}
Log.e("_Customer", Thread.currentThread().getName() + "----消费者------" + consume());
try {Thread.sleep(1000);} catch (InterruptedException i) {}
p.setFlag(false);
Products.class.notifyAll();
}
}
}
}
//测试
main(){
Products product = new Products();
ProductFactory pro = new ProductFactory(product);
_Customer con = new _Customer(product);
Thread pro_t1 = new Thread(pro); //生产线程1
Thread pro_t2 = new Thread(pro); //生产线程2
Thread con_t1 = new Thread(con); //消费线程1
Thread con_t2 = new Thread(con); //消费线程2
pro_t1.start();
pro_t2.start();
con_t1.start();
con_t2.start();
}
以下是采用Lock和Conditon重构后的代码,使用的是signal()唤醒对方线程的方法。
//产品
public class Products_1 {
private String name;
private int count = 1;
private boolean flag = false;//为wait和notify提供判断标记
//为生产者和消费者提供同一个锁对象和同一个condition
public Lock lock = new ReentrantLock();
public Condition pro_con = lock.newCondition();
public Condition con_con = lock.newCondition();
}
//生产者
package com.my_project.test_more_thread.product;
import android.util.Log;
import com.my_project.test_more_thread.goods.Products_1;
/**
* Created by Administrator on 2019\2\25 0025.
*/
public class ProductFactory_1 implements Runnable {
private Products_1 p;
public ProductFactory_1(Products_1 product_1) {
this.p = product_1;
}
//对外暴露生产产品的方法
public void startProduct(String name) {
p.setName(name + p.getCount());
p.setCount(p.getCount() + 1);
}
@Override
public void run() {
while (true) {
//使用lock锁
p.lock.lock();
try {
while (p.isFlag()){
//没有在消费,说明仓库没有产品了,生产者不需要睡眠,赶紧醒来工作了
try {p.pro_con.await();} catch (InterruptedException e) {e.printStackTrace();}
}
startProduct("汉堡");
Log.e("ProductFactory", Thread.currentThread().getName() + "----生产者------" + p.getName());
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
p.setFlag(true);
p.con_con.signal();
} catch (Exception e) {
} finally {
p.lock.unlock();
}
}
}
}
//消费者
package com.my_project.test_more_thread.customer;
import android.util.Log;
import com.my_project.test_more_thread.goods.Products_1;
/**
* Created by Administrator on 2019\2\25 0025.
*/
public class _Customer_1 implements Runnable {
private Products_1 p;
public _Customer_1(Products_1 product_1) {
this.p = product_1;
}
//提供消费产品的方法
public String consume() {
return p.getName();
}
@Override
public void run() {
while (true) {
p.lock.lock();
try {
while (!p.isFlag()){
try {p.con_con.await();} catch (InterruptedException e) {e.printStackTrace();}
}
Log.e("_Customer", Thread.currentThread().getName() + "----消费者------" + consume());
try {Thread.sleep(1000);} catch (InterruptedException i) {}
p.setFlag(false);
p.pro_con.signal();
} catch (Exception e) {
} finally {
p.lock.unlock();
}
}
}
}
//测试
main(){
Products_1 product = new Products_1();
ProductFactory_1 pro = new ProductFactory_1(product);
_Customer_1 con = new _Customer_1(product);
Thread pro_t1 = new Thread(pro); //生产线程1
Thread pro_t2 = new Thread(pro); //生产线程2
Thread con_t1 = new Thread(con); //消费线程1
Thread con_t2 = new Thread(con); //消费线程2
pro_t1.start();
pro_t2.start();
con_t1.start();
con_t2.start();
}
从上面的测试代码我们能明显看出来,lock会均匀切换两个线程,而synchronized同步代码块则有点听天由命的感觉,lock加锁后的两个生产线程,
关于多生产、多消费问题做个总结:
(1).解决某一方多线程不同步的方案是使用while(flag)来判断是否wait;
(2).解决双方死锁问题的方案是唤醒对方,可以使用notifyAll(),signalAll()或对方监视器的signal()方法。
有多个生产者线程,多个消费者线程,生产者将生产的面包放进篮子(集合或数组)里,消费者从篮子里取出面包。生产者判断继续生产的依据是篮子已经满了,消费者判断继续消费的依据是篮子是否空了。此外,当消费者取出面包后,对应的位置又空了,生产者可以回头从篮子的起始位置继续生产,这可以通过重置篮子的指针来实现。
在这个模式里,除了描述生产者、消费者、面包,还需要描述篮子这个容器。假设使用数组作为容器,生产者每生产一个,生产指针向后移位,消费者每消费一个,消费指针向后移位。
//产品
public class Products_2 {
private String name;
public Products_2(String name, int i) {
this.name = name + i;
}
public String getName() {
return name;
}
}
//生产者
public class ProductFactory_2 implements Runnable {
public static int num = 1;
private _Basket mList;
public ProductFactory_2(_Basket _basket) {
this.mList = _basket;
}
@Override
public void run() {
while (true) {
mList.in();
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
}
}
}
//消费者
public class _Customer_2 implements Runnable {
private _Basket mList;
public _Customer_2(_Basket b) {
this.mList = b;
}
@Override
public void run() {
while (true) {
Products_2 out = mList.out();
try {Thread.sleep(1000);} catch (InterruptedException i) {}
}
}
}
//放产品的容器篮子
class Basket {
private Products_2 [] arr;
//the size of basket
Basket(int size){
arr = new Products_2 [size];
}
//the pointer of in and out
private int in_ptr,out_ptr;
//how many breads left in basket
private int left;
private Lock lock = new ReentrantLock();
private Condition full = lock.newCondition();
private Condition empty = lock.newCondition();
//bread into basket
public void in(){
lock.lock();
try{
while(left == arr.length){
try{full.await();} catch (InterruptedException i) {i.printStackTrace();}
}
arr[in_ptr] = new Products_2 ("MianBao",Products_2 .num++);
System.out.println("Put the Products_2 : "+arr[in_ptr].getName()+"------into Products_2 ["+in_ptr+"]");
left++;
if(++in_ptr == arr.length){in_ptr = 0;}
empty.signal();
} finally {
lock.unlock();
}
}
//bread out from basket
public Products_2 out(){
lock.lock();
try{
while(left == 0){
try{empty.await();} catch (InterruptedException i) {i.printStackTrace();}
}
Products_2 out_bread = arr[out_ptr];
System.out.println("Get the bread: "+out_bread.getName()+"-----------from basket["+out_ptr+"]");
left--;
if(++out_ptr == arr.length){out_ptr = 0;}
full.signal();
return out_bread;
} finally {
lock.unlock();
}
}
}
//上边是容器篮子中只要有产品,消费者立马去消费 ,下边再来一种 容器满了生产者wait 消费者唤醒,为0后
//消费者wait,生产者唤醒
public class _Basket {
private Products_2[] arr;
//the size of basket
public _Basket(int size) {
arr = new Products_2[size];
}
//the pointer of in and out
private int in_ptr, out_ptr;
//how many product left in basket
private int left;
private volatile boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition pro_lock = lock.newCondition();
private Condition cus_lock = lock.newCondition();
//product into basket
public void in() {
lock.lock();
try {
while (flag) {
try {
cus_lock.signal();
Log.e("in----------", "我要开始睡了" + left + out_ptr);
pro_lock.await();
Log.e("in----------", "我被叫醒来了" + left + out_ptr);
} catch (InterruptedException i) {
i.printStackTrace();
}
}
arr[in_ptr] = new Products_2("汉堡", ProductFactory_2.num++);
Log.e("Put the product: ", arr[in_ptr].getName() + "------into basket[" + in_ptr + "]");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
left++;
Log.e("in----------", "left------" + left);
if (++in_ptr == arr.length) {
in_ptr = 0;
flag = true;
}
} finally {
lock.unlock();
}
}
//bread out from basket
public Products_2 out() {
lock.lock();
try {
while (!flag) {
try {
pro_lock.signal();
Log.e("out----------", "我要开始睡了" + left + out_ptr);
cus_lock.await();
Log.e("out----------", "我被叫醒来了" + left + out_ptr);
} catch (InterruptedException i) {
i.printStackTrace();
}
}
Products_2 out_Products_2 = arr[out_ptr];
Log.e("Get the bread:", out_Products_2.getName() + "-----------from basket[" + out_ptr + "]");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
left--;
Log.e("out----------", "left------" + left);
if (++out_ptr == arr.length) {
out_ptr = 0;
}
if (left == 0) {
flag = false;
}
return out_Products_2;
} finally {
lock.unlock();
}
}
}
这里涉及了消费者、生产者、产品和篮子,其中产品和篮子是多线程共同操作的资源,生产者线程生产产品放进篮子,消费者线程从篮子中取出产品。理想的代码是将生产任务和消费任务都封装在资源类中,因为产品是篮子容器的元素,所以不适合封装到产品类中,而且封装到篮子中,能更方便地操作容器。
注意,一定要将所有涉及资源操作的代码都放进锁的内部,否则会产生多线程不同步问题。例如,在Products类中定义了生产产品的方法produce(),然后将其作为放进篮子的方法basket.in()的参数,即basket.in(producer()),这是错误的行为,因为produce()是在锁的外部执行后才传递给in()方法的。