Synchronized同步锁版:
/*
* 线程之间的通信问题,生产者消费者问题
* 线程交替操作,A,B,操作同一个变量-->num=0
* A-->num+1
* B-->num-1
* */
public class PC {
public static void main(String[] args) {
Buffer buffer=new Buffer();
//生产线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
//消费线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//缓冲区
class Buffer{
private int number=0;
//生产者
public synchronized void increment() throws InterruptedException {
if(number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"-->"+number);
//通知消费者消费
this.notifyAll();
}
//消费者
public synchronized void decrement() throws InterruptedException {
if(number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"-->"+number);
//通知生产者生产
this.notifyAll();
}
}
如果存在多个生产者,消费者:
1:问题:在没有被通知、中断或超时的情况下,线程还可以唤醒一个所谓的虚假唤醒 (spurious wakeup)。
2:虚假唤醒:虚假唤醒是一种现象,它只会出现在多线程环境中,指的是在多线程环境下,多个线程等待在同一个条件 上,等到条件满足时,所有等待的线程都被唤醒,但由于多个线程执行的顺序不同,后面竞争到锁的线程在获得时 间片时条件已经不再满足,线程应该继续等待但是却继续往下运行的一种现象。
3:解决方法:
将if改为while,等待应总是发生在循环中,条件不满足时会让线程继续等待。
注意:不能将notifyAll()
改为notify()
,这样还是有机率,生产者唤醒生产者,消费者唤醒消费者,产生虚假唤醒。
public class PC {
public static void main(String[] args) {
Buffer buffer=new Buffer();
//生产线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
//消费线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
//生产线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
//消费线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//缓冲区
class Buffer{
private int number=0;
//生产者
public synchronized void increment() throws InterruptedException {
while (number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"-->"+number);
//通知消费者消费
this.notifyAll();
}
//消费者
public synchronized void decrement() throws InterruptedException {
while (number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"-->"+number);
//通知生产者生产
this.notifyAll();
}
}
Lock锁版:
通过Lock可以new出Condition对象,使用Condition对象的await(类似于wait),signal(类似于notify)方法。
Synchronized与Lock对比:
示例:
public class PCLock {
public static void main(String[] args) {
BufferLock buffer=new BufferLock();
//生产线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
//消费线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
//生产线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
//消费线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
buffer.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class BufferLock{
private int number=0;
//Lock锁
Lock lock=new ReentrantLock();
//lock锁的两个Condition对象
Condition Producer=lock.newCondition();
Condition Consumer=lock.newCondition();
//生产者
public void increment() throws InterruptedException {
lock.lock();
try {
//业务代码
while (number!=0){
//等待
Producer.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"-->"+number);
//通知消费者消费
Consumer.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//消费者
public void decrement() throws InterruptedException {
lock.lock();
try {
//业务代码
while (number==0){
//等待
Consumer.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"-->"+number);
//通知生产者生产
Producer.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition精确通知唤醒线程:
按照顺序唤醒对应的线程:
public class PCCondition {
public static void main(String[] args) {
BufferCondition buffer=new BufferCondition();
new Thread(()->{
for (int i = 0; i < 10; i++) {
buffer.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
buffer.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
buffer.printC();
}
},"C").start();
}
}
class BufferCondition{
private Lock lock=new ReentrantLock();
private Condition conditionA=lock.newCondition();
private Condition conditionB=lock.newCondition();
private Condition conditionC=lock.newCondition();
private int number=1;//1A 2B 3C
public void printA(){
lock.lock();
try {
while (number!=1){
//等待
conditionA.await();
}
System.out.println(Thread.currentThread().getName()+"打印线程A");
//唤醒
number=2;
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number!=2){
//等待
conditionB.await();
}
System.out.println(Thread.currentThread().getName()+"打印线程B");
//唤醒
number=3;
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number!=3){
//等待
conditionC.await();
}
System.out.println(Thread.currentThread().getName()+"打印线程C");
//唤醒
number=1;
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}