1、synchronized版
在生产线程执行生产proucet()方法时先判断总量,如果sum>0,即库存里还有没有被消费线程消费的食物就等待wait(),wait()方法会释放锁,让消费线程先占用锁去调用消费方法。
同理,消费线程进入判断,如果sum=0,即生产者还没有生产出食物,或者食物在这一刻被消费线程消费光了,那就需要等待消费者生产食物。
判断为什么要用while循环而不是if?
防止虚假唤醒。比如当个第一个生产者A进入product()方法,if判断sum>0的话,就执行wait()方法释放锁,这时比如生产者C获取到了锁,与A一样也等待,释放锁,这时消费者线程B获取到锁,执行了sum-1操作,之后执行notifyAll()方法唤醒线程,这时A线程或C线程被唤醒后,这时已经执行完了if判断,就会直接执行下面的代码。
if和while的区别:
- if判断商品数量>0时,线程被阻塞,这时if判断就完成了,线程被唤醒后直接执行剩余操作
- while判断商品数量>0时,线程阻塞,这时while循环没有被完成,当线程被唤醒后,会继续while判断商品数量
package juctest;
public class ProcusLockTest {
public static void main(String[] args) {
Food food =new Food();
new Thread(()->{
for(int i=1;i<=10;i++){
food.product();
}
},"A").start();
new Thread(()->{
for(int i=1;i<=10;i++){
food.eat();
}
},"B").start();
new Thread(()->{
for(int i=1;i<=10;i++){
food.product();
}
},"C").start();
new Thread(()->{
for(int i=1;i<=10;i++){
food.eat();
}
},"D").start();
}
}
//线程共享资源
class Food{
private int num = 0;
public synchronized void product() {//生产
while(num>0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"生产了,还剩余"+num);
this.notifyAll();
}
public synchronized void eat() {//消费
while(num==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName()+"吃了,还剩余"+num);
this.notifyAll();
}
}
2、Lock锁版
Lock锁对象可以获得Condition监视器对象
condition.await();=======》可以替换this.wait()
condition.signalAll();========>可以替换this.notifiyAll()
package juctest;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProcusLockTest {
public static void main(String[] args) {
FoodLock food =new FoodLock();
new Thread(()->{
for(int i=1;i<=10;i++){
food.product();
}
},"A").start();
new Thread(()->{
for(int i=1;i<=10;i++){
food.eat();
}
},"B").start();
new Thread(()->{
for(int i=1;i<=10;i++){
food.product();
}
},"C").start();
new Thread(()->{
for(int i=1;i<=10;i++){
food.eat();
}
},"D").start();
}
}
//线程共享资源
class FoodLock{
private int num = 0;
//获取锁
private Lock lock =new ReentrantLock();
//获取锁的监视器对象
Condition condition = lock.newCondition();
public void product() {//生产
lock.lock();//加锁
try {
while(num>0){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"生产了,还剩余"+num);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void eat() {//消费
lock.lock();//加锁
try {
while(num==0){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName()+"消费了,还剩余"+num);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
3、通过多个监视器Condition实现线程间精准通信
现在有两个生产线程A和B,一个消费线程C,我们需要在AB生产完之后,唤醒C消费。
代码如下
condition2.signal();//唤醒condition2的等待线程
public class ProcusLockTest {
public static void main(String[] args) {
FoodLock food =new FoodLock();
new Thread(()->{
for(int i=1;i<=10;i++){
food.product();
}
},"A").start();
new Thread(()->{
for(int i=1;i<=10;i++){
food.product();
}
},"B").start();
new Thread(()->{
for(int i=1;i<=10;i++){
food.eat();
}
},"C").start();
}
}
//线程共享资源
class FoodLock{
private int num = 0;
//获取锁
private Lock lock =new ReentrantLock();
//获取锁的监视器对象
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
public void product() {//生产
lock.lock();//加锁
try {
while(num==2){
try {
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"生产了,还剩余"+num);
condition2.signal();//唤醒消费线程C
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void eat() {//消费
lock.lock();//加锁
try {
while(num==0){
try {
condition2.await();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num = num-2;
System.out.println("eatC======"+Thread.currentThread().getName()+"消费了两个,还剩余"+num);
condition1.signal();//唤醒A或者B
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}