java多线程中wait方法要在while中调用而非if中

本文详细解析了Java多线程中wait与notify的工作原理,并通过具体示例阐述了if与while条件下线程被唤醒后的不同行为,强调了在多线程环境下合理使用这些方法的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开门见山直说:

       在java多线程情况下,当线程被wait后,又通过notify方法唤醒时,在if情况下被唤醒,程序从哪里wait的就从哪里继续执行,在while的情况下被唤醒,程序依旧会从哪里wait的继续往下执行,但是,在执行之前会先对循环条件进行判断,满足条件继续wait,不满足条件继续执行。而if语句不会再对条件进行判断,直接继续执行。所以在多线程情况下,可能会有多个线程再对资源进行修改,当被wait的线程被唤醒时,有其他线程已经将资源进行修改过了,导致 while的条件发生变化(不再满足)。而刚唤醒的线程根本不知道,所以导致异常。

实例:

class SynStack
{
    private char[] data = new char[6];
    private int cnt = 0; //表示数组有效元素的个数

    public synchronized void push(char ch)
    {
        if (cnt >= data.length)
        {
            try
            {
                System.out.println("生产线程"+Thread.currentThread().getName()+"准备休眠");
                this.wait();
                System.out.println("生产线程"+Thread.currentThread().getName()+"休眠结束了");
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        this.notify();
        data[cnt] = ch;
        ++cnt;
        System.out.printf("生产线程"+Thread.currentThread().getName()+"正在生产第%d个产品,该产品是: %c\n", cnt, ch);
    }

    public synchronized char pop()
    {
        char ch;
        if (cnt <= 0)
        {
            try
            {
                System.out.println("消费线程"+Thread.currentThread().getName()+"准备休眠");
                this.wait();
                System.out.println("消费线程"+Thread.currentThread().getName()+"休眠结束了");
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        this.notify();
        ch = data[cnt-1];
        System.out.printf("消费线程"+Thread.currentThread().getName()+"正在消费第%d个产品,该产品是: %c\n", cnt, ch);
        --cnt;
        return ch;
    }
}

class Producer implements Runnable
{
    private SynStack ss = null;
    public Producer(SynStack ss)
    {
        this.ss = ss;
    }

    public void run()
    {
        char ch;
        for (int i=0; i<10; ++i)
        {
            ch = (char)('a'+i);
            ss.push(ch);
        }
    }
}

class Consumer implements Runnable
{
    private SynStack ss = null;

    public Consumer(SynStack ss)
    {
        this.ss = ss;
    }

    public void run()
    {
        for (int i=0; i<10; ++i)
        {
            ss.pop();
        }
    }
}


public class TestPC2 {

    public static void main(String[] args)
    {
        SynStack ss = new SynStack();
        Producer p = new Producer(ss);
        Consumer c = new Consumer(ss);


        Thread t1 = new Thread(p);
        t1.setName("1号");
        t1.start();
        /*Thread t2 = new Thread(p);
        t2.setName("2号");
        t2.start();*/

        Thread t6 = new Thread(c);
        t6.setName("3号");
        t6.start();
        Thread t7 = new Thread(c);
        t7.setName("4号");
        t7.start();
    }

}

补充:

       在思考上面的问题的时候,发现了一个很好玩的东西,就是通过new ArrayList<String>()来声明一个新对象的时候,其实这个对象不是空的,里面是有一个值的也就是anyString,但是这个值是不计数的,我们通过size()获取的元素大小是直接获取的ArrayList的size成员变量;

/**
 * The size of the ArrayList (the number of elements it contains).
 *
 * @serial
 */
private int size;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值