本文中举例的2种写法可以实现同步问题,但会出现虚假唤醒的问题,详细解析和解决方案请看正文
管程法
/*
* 管程法
* */
public class TestGuan {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Product(synContainer).start();
new Consumer(synContainer).start();
}
}
//##################################################################################
//生产者
class Product extends Thread{
SynContainer synContainer;
public Product(SynContainer synContainer){
this.synContainer = synContainer;
}
//生产
@Override
public void run() {
for (int i = 1; i < 101; i++) {
synContainer.push(new Chicken(i));
}
}
}
//##################################################################################
//消费者
class Consumer extends Thread{
SynContainer synContainer;
public Consumer(SynContainer synContainer) {
this.synContainer = synContainer;
}
//消费
@Override
public void run() {
for (int i = 1; i < 101; i++) {
synContainer.pop();
}
}
}
//##################################################################################
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//##################################################################################
//缓冲区
class SynContainer {
//需要一个容器大小
Chicken[] chickens = new Chicken[9];
//容器计数器
int count = 0;
//产品上架
public synchronized void push(Chicken chicken){
//如果容器满了,需要等待消费
if (count == chickens.length){ //判断count个数十否为10
//通知消费者消费,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,就添加
chickens[count] = chicken;
count ++;
System.out.println("添加了第"+chicken.id+"个");
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断还有没有可以消费的产品
if (count == 0) {
//等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count --;
System.out.println("消费了====>第"+(chickens[count].id)+"个");
Chicken chicken = chickens[count];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
信号灯法
public class TestPC2 {
public static void main(String[] args) {
Common common = new Common();
new Productor(common).start();
new Consumer1(common).start();
}
}
//##################################################################################
//生产者
class Productor extends Thread{
Common common;
public Productor(Common common) {
this.common = common;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
common.push(i+"");
}
}
}
//##################################################################################
//消费者
class Consumer1 extends Thread{
Common common;
public Consumer1(Common common) {
this.common = common;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
common.pop();
}
}
}
//##################################################################################
//产品
class Products{
String name;
public Products() {
}
}
//##################################################################################
//公共资源类
class Common{
private boolean flag = true;
Products p = new Products();
public synchronized void push(String name){
while (!flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
p.name = name;
System.out.println("生产了产品"+p.name);
flag = !flag;
notifyAll();
}
public synchronized void pop(){
while (flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费了产品==>"+p.name);
flag = !flag;
notifyAll();
}
}
出现的问题
例如管程法中,在只有一个生产者和消费者时没有问题
但是如果有2个生产者线程和2个消费者线程就会和没有加锁一样会出现溢出的情况
因为在此处用了if判断,if只运行一次,下次唤醒时会继续向下执行而不去判断条件是否成立
这就是虚假唤醒问题。
在jdk帮助文档中的Object类中的 wait() 方法也有介绍