需求:同时两个线程生产口罩,同时两个线程消费口罩,且必须生产了之后才能消费;
方法一 : 添加同步代码块
class ProductConsumerDemo {
public static void main(String[] args) {
Mask k = new Mask();
//这里所有的Runnable都传入了同一个参数k,是为了保证标志位一致
Thread productThread1 = new Thread(new ProductRunnable(k));
Thread productThread2 = new Thread(new ProductRunnable(k));
Thread consumerThread1 = new Thread(new ConsumerRunnable(k));
Thread consumerThread2 = new Thread(new ConsumerRunnable(k));
productThread1.start();
productThread2.start();
consumerThread1.start();
consumerThread2.start();
}
}
class ProductRunnable implements Runnable {
public Mask k;
ProductRunnable(Mask k) {
this.k=k;
}
public void run() {
int x=0;
while(true) {
synchronized(k) {
//这里不能用if,当wait的线程被唤醒时,会接着继续往下面执行,如果用if,接着往下执行就直接跳出了if语句,
//并没有去判断flag的值,这样会导致被wait的生产线程和消费线程都开始往下执行,会导致数据出错,实际我们只需要生产的线程开始执行,
//而被唤醒的消费的线程被唤醒后应该继续wait,所以这个地方用while而不是if。
while(k.flag) {
try {k.wait();} catch(Exception e) {}
}
if(x==0) {
k.country="中国";
k.model="N95";
} else {
k.country="japan";
k.model="N20";
}
k.num=k.num+1;
System.out.println(k.country + "生成了第-------" + k.num + "--------"+k.model);
x=(x+1)%2;
k.flag=true; //生产了一个口罩
k.notifyAll();
//如果使用notify,而notify只会唤醒等待线程池中第一个被wait的线程,因此可能notify的也是另外一个ProductThread线程,
//那么当唤醒的ProductThread执行到while(k.flag)时,也会wait,这时就会导致所有线程都处于wait状态,
//因此这里使用的notifyAll(),这样可以唤醒所有wait的线程.
}
}
}
}
class ConsumerRunnable implements Runnable {
public Mask k;
ConsumerRunnable(Mask k) {
this.k=k;
}
public void run() {
while(true) {
synchronized(k) {
while(!k.flag){
try {k.wait();} catch(Exception e) {}
}
System.out.println("意大利消费了第==="+ k.num + "个" + k.country+"的===" + k.model);
k.flag=false; //代表消费一个口罩,因此将标志位flag设置为false
k.notifyAll();
}
}
}
}
class Mask {
public String country;
public String model;
public int num;
public boolean flag;
}
方法二:在方法上加锁
import java.util.concurrent.locks.*;
class ProductConsumerDemo {
public static void main(String[] args) {
Mask k = new Mask();
ProductRunnable pro = new ProductRunnable(k);
ConsumerRunnable con = new ConsumerRunnable(k);
Thread productThread1 = new Thread(pro);
Thread productThread2 = new Thread(pro);
Thread consumerThread1 = new Thread(con);
Thread consumerThread2 = new Thread(con);
productThread1.start();
productThread2.start();
consumerThread1.start();
consumerThread2.start();
}
}
class ProductRunnable implements Runnable {
public Mask k;
ProductRunnable(Mask k) {
this.k=k;
}
public void run() {
int x=0;
while(true) {
if(x==0) {
k.set("中国");
} else {
k.set("japan");
}
x=(x+1)%2;
}
}
}
class ConsumerRunnable implements Runnable {
public Mask k;
ConsumerRunnable(Mask k) {
this.k=k;
}
public void run() {
while(true) {
k.out();
}
}
}
class Mask {
public String country;
public int num;
public boolean flag;
//方法上加锁,同一时间只有一个线程能调用set方法,如果不加锁,两个线程同时设置数据,会导致数据错乱
public synchronized void set(String country) {
while(flag)
try {
wait();
} catch(Exception e) {}
this.country=country;
System.out.println(Thread.currentThread() + "+++"+country + "+++" + ++num);
flag=true;
notifyAll();
}
//方法上加锁,同一时间只有一个线程能调用out方法
public synchronized void out() {
while(!flag)
try {
wait();
} catch(Exception e) {}
System.out.println(Thread.currentThread() + "---------"+country + "------------" + num);
flag = false;
notifyAll();
}
}
notifyAll同样存在唤醒同类线程的问题,这样就会去争抢资源,那么我们能不能在ProductThread执行notify时只去唤醒ConsumerThread线程了?
改进版:使用Lock和Condition
import java.util.concurrent.locks.*;
class ProductConsumerDemo {
public static void main(String[] args) {
Mask k = new Mask();
ProductRunnable pro = new ProductRunnable(k);
ConsumerRunnable con = new ConsumerRunnable(k);
Thread productThread1 = new Thread(pro);
Thread productThread2 = new Thread(pro);
Thread consumerThread1 = new Thread(con);
Thread consumerThread2 = new Thread(con);
productThread1.start();
productThread2.start();
consumerThread1.start();
consumerThread2.start();
}
}
class ProductRunnable implements Runnable {
public Mask k;
ProductRunnable(Mask k) {
this.k=k;
}
public void run() {
int x=0;
while(true) {
try {
if(x==0) {
k.set("中国");
} else {
k.set("japan");
}
x=(x+1)%2;
} catch(InterruptedException e) {
}
}
}
}
class ConsumerRunnable implements Runnable {
public Mask k;
ConsumerRunnable(Mask k) {
this.k=k;
}
public void run() {
while(true) {
try {
k.out();
} catch(InterruptedException e) {
}
}
}
}
class Mask {
public String country;
public int num;
public boolean flag;
Lock lock = new ReentrantLock();
Condition pro_condition = lock.newCondition();
Condition con_condition = lock.newCondition();
public void set(String country) throws InterruptedException {
//给set方法上锁
lock.lock();
try {
while(flag){
//线程wait后会释放锁
pro_condition.await();
}
this.country=country;
System.out.println(Thread.currentThread() + "+++"+country + "+++" + ++num);
flag=true;
con_condition.signal();
} finally {
//当set方法执行完毕后会释放锁
lock.unlock();
}
}
public void out() throws InterruptedException {
lock.lock();
try {
while(!flag) con_condition.await();
System.out.println(Thread.currentThread() + "---------"+country + "------------" + num);
flag = false;
pro_condition.signal();
} finally{
lock.unlock();
}
}
}