1、线程通信-生产者消费者问题
生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。
Java中线程通信协作的最常见的两种方式:
一.syncrhoized加锁的线程的Object类的wait()/notify()/notifyAll()
二.ReentrantLock类加锁的线程的Condition类的await()/signal()/signalAll()
Synchronized 普通版
public class Demo3 {
public static void main(String[] args) {
Factory factory = new Factory();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
factory.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"生产者").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
factory.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"消费者").start();
}
}
class Factory{
private int num = 0;
//生产者生产,生产完成之后通知消费者消费
public synchronized void produce() throws InterruptedException {
if (num!=0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"生产,当前num="+num);
this.notifyAll();
}
//消费者消费,消费完通知生产者生产
public synchronized void consume() throws InterruptedException {
if (num==0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"消费,当前num="+num);
this.notifyAll();
}
}
输出结果
两个线程可以,四个线程可以交替执行吗?不能,会产生虚假唤醒问题
public class Demo3 {
public static void main(String[] args) {
Factory factory = new Factory();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
factory.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"生产者1").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
factory.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"生产者2").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
factory.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"消费者1").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
factory.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"消费者2").start();
}
}
class Factory{
private int num = 0;
//生产者生产,生产完成之后通知消费者消费
public synchronized void produce() throws InterruptedException {
if (num!=0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"生产,当前num="+num);
this.notifyAll();
}
//消费者消费,消费完通知生产者生产
public synchronized void consume() throws InterruptedException {
if (num==0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"消费,当前num="+num);
this.notifyAll();
}
}
输出:
由此发现数量出现了问题
解决办法
由此可见多个线程之间若想交替执行,不能用if去做判断,应该为while,我们看一下当为while的时候
//生产者生产,生产完成之后通知消费者消费
public synchronized void produce() throws InterruptedException {
while (num!=0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"生产,当前num="+num);
this.notifyAll();
}
//消费者消费,消费完通知生产者生产
public synchronized void consume() throws InterruptedException {
while (num==0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"消费,当前num="+num);
this.notifyAll();
}
输出结果
Lock模式下实现精准唤醒
package demo.communication;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
实现线程交替执行!
主要的实现目标:精准的唤醒线程!
三个线程:A B C
三个方法:A p5 B p10 C p15 依次循环
*/
public class Demo4 {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.print5();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.print10();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.print15();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
}
}
// 资源类
class Data2{
private int number = 1; // 1A 2B 3C
private Lock lock = new ReentrantLock();
// 实现精准访问
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void print5() throws InterruptedException {
lock.lock();
try {
// 判断
while (number!=1){
condition1.await();
}
// 执行
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 通知第二个线程干活!
number = 2;
condition2.signal(); // 唤醒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 一定要解锁
}
}
public void print10() throws InterruptedException {
lock.lock();
try {
// 判断
while (number!=2){
condition2.await();
}
// 执行
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 通知3干活
number = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15() throws InterruptedException {
lock.lock();
try {
// 判断
while (number!=3){
condition3.await();
}
// 执行
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
// 通知 1 干活
number = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}