在Java的等待唤醒机制中,为什么锁对象不能是业务操作的对象?

举个很简单的例子,厨师和吃客,厨师做出一道菜,吃客吃一道菜;按常理推,菜的数量应为锁,从而操作线程唤醒或等待,但事实是food不能成为synchronized的参数,必须在Desk中定义一个Object lock并传入synchronized,程序才能跑的通,很疑惑,请大神解答!
正确代码如下,错误代码放在注释中。

//餐桌
public class Desk  {
    public static Integer food = 0;// 食物,1代表有食物,0代表无食物
    public static Object lock = new Object();
}

厨师类

public class Cook extends Thread {
    @Override
    public void run() {
        synchronized (Desk.lock) {//这里必须使用Object类对象,而使用Desk.food就报错
            while (true) {
                if (Desk.food == 1) {
                    try {
                        Desk.lock.wait();//这里必须用lock对象,而使用Desk.food就报错
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    Desk.food += 1;
                    System.out.println("厨师做菜" + Desk.food);
                    Desk.lock.notifyAll();//唤起消费者线程,这里必须用lock对象,而使用Desk.food就报错
                }
            }
        }
    }
}

吃客类

public class Foodie extends Thread {
    @Override
    public void run() {
        synchronized (Desk.lock) {//这里必须使用Object类对象,而使用Desk.food就报错
            while (true) {
                if (Desk.food == 0) {
                    try {
                        Desk.lock.wait();//当前线程等待,这里必须用lock对象,而使用Desk.food就报错
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    Desk.food -= 1;
                    System.out.println("我吃了" + Desk.food);
                    Desk.lock.notifyAll();//唤起厨师线程,这里必须用lock对象,而使用Desk.food就报错
                }
            }
        }
    }
}

main方法

public static void main(String[] args){
    Cook cook =new Cook();
    Foodie foodie = new Foodie();
    Thread c = new Thread(cook,"厨师线程");
    Thread f = new Thread(foodie,"消费者线程");
    c.start();
    f.start();
}
阅读 670
avatarAI BotBETA

在Java中,等待唤醒机制是基于对象监视器(Object Monitor)实现的,通过synchronized关键字和锁对象来实现线程之间的同步。在这个问题中,为什么锁对象不能是业务操作的对象(比如食物对象food),主要有以下几个原因:

  1. 对象的非线程安全性:业务对象(比如这里的food对象)通常不是线程安全的。如果多个线程同时访问并修改同一个业务对象,可能会导致数据不一致的问题。为了保证线程安全性,我们需要使用一个专门的锁对象来控制对业务对象的访问。
  2. 锁的粒度:使用专门的锁对象(比如一个普通的Object实例)作为锁,可以控制对业务对象的访问。这样可以保证同一时间只有一个线程可以访问业务对象,从而避免了多线程并发修改的问题。
  3. 灵活性:使用专门的锁对象,可以灵活地控制对业务对象的访问。比如在本例中,我们使用一个共享的锁对象(lock)来控制对食物对象的访问。如果需要增加其他共享资源(比如凳子),只需要在这个锁对象上进行同步即可,不需要为每个资源都创建一个锁。

因此,为了确保线程安全、控制并发访问和保持灵活性,通常使用一个专门的锁对象来作为synchronized的参数,而不是业务操作的对象。

1 个回答
  1. synchronized 的锁在 对象
  2. 你的 food 是一个 Integer 类型对象的引用
  3. 设想这样一个场景:

    1. food 是 0,锁住了消费者
    2. 生产者把 food 设置成了 1
    3. 把消费者锁住的锁是 0,并没有人释放 1,所以消费者就一直被锁住了
  4. 简而言之,专门的锁交给专门的对象做
  5. 顺便一说,默认情况下 -255 到 255 的 Integer 对象都是复用的,如果你在某个地方用 0 作为锁,可能会把其他地方用 0 做锁的逻辑锁住…String 也有类似的问题,所以一般就随手 new Object 当锁用
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题