3.4wait的条件发生变化导致逻辑错误

本文通过一个Java线程同步的实例,展示了如何正确使用synchronized关键字和wait/notify机制来避免多线程环境中出现的问题。特别强调了在条件发生变化时,如何避免因条件检查不当而导致的异常。

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

若wait的条件需要发生变化是,需要注意程序逻辑的变化

下面有个错误例子

package com.myObject;

public class AddObject {
    private String lock;

    public AddObject(String lock) {
        this.lock = lock;
    }

    public void add() {
        synchronized (lock) {
            ValueObject.list.add("anyStr");
            lock.notifyAll();
        }
    }
}
package com.myObject;

public class SubObject {
    private String lock;

    public SubObject(String lock) {
        this.lock = lock;
    }

    public void sub(){
        synchronized(lock){
            try {
                if(ValueObject.list.size()==0){//注意wait条件的变化
                    System.out.println(Thread.currentThread().getName()+" wait begin " + System.currentTimeMillis());
                    lock.wait();
                    System.out.println(Thread.currentThread().getName()+" wait end " + System.currentTimeMillis());
                }
                ValueObject.list.remove(0);
                System.out.println("list size "+ValueObject.list.size());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.myObject;

import java.util.ArrayList;
import java.util.List;

public class ValueObject {
public static List list= new ArrayList();
}
package com.myThread;

import com.myObject.AddObject;

public class Thread1a extends Thread {
    AddObject addObject;

    public Thread1a(AddObject addObject) {
        this.addObject = addObject;
    }

    @Override
    public void run() {
        addObject.add();
    }
}
package com.myThread;

import com.myObject.SubObject;

public class Thread1b extends Thread {
    SubObject subObject;

    public Thread1b(SubObject subObject) {
        this.subObject = subObject;
    }

    @Override
    public void run() {
        subObject.sub();
    }
}
package com.test;

import com.myObject.AddObject;
import com.myObject.SubObject;
import com.myThread.Thread1a;
import com.myThread.Thread1b;

public class Test1 {
    public static void main(String[] args) throws InterruptedException{
        String lock = new String("hello");
        AddObject addObject = new AddObject(lock);
        SubObject subObject = new SubObject(lock);
        Thread1a thread1a = new Thread1a(addObject);
        thread1a.setName("add");
        Thread1b thread1b1 = new Thread1b(subObject);
        Thread1b thread1b2 = new Thread1b(subObject);
        thread1b1.setName("sub1");
        thread1b2.setName("sub2");
        thread1b1.start();
        thread1b2.start();
        Thread.sleep(2000);
        thread1a.start();

    }
}

打印结果

sub2 wait begin 1453104941268
sub1 wait begin 1453104941268
sub1 wait end 1453104943268
Exception in thread "sub2" list size 0
sub2 wait end 1453104943269
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.RangeCheck(ArrayList.java:547)
at java.util.ArrayList.remove(ArrayList.java:387)
at com.myObject.SubObject.sub(SubObject.java:18)
at com.myThread.Thread1b.run(Thread1b.java:14)

结果分析:当sub1和sub2分别在wait之前,进行了一次判断,那时的条件都是“ValueObject.list.size()==0”,但被唤醒之后并没有再次进行判断,就继续往下执行了,然后判断的条件已经发生改变了,所以导致List的索引溢出

解决方法:这里的解决方法时使用while,把if改为while

打印结果

sub2 wait begin 1453106550825
sub1 wait begin 1453106550825
sub1 wait end 1453106552825
list size 0
sub2 wait end 1453106552826
sub2 wait begin 1453106552826

结果分析:sub1和sub2都被唤醒,sub1抢先得到资源然后使得“ValueObject.list.size()==1”退出循环,sub2紧接着得到资源,但sub2得到资源时sub1已经把ValueObject.list.size()变为0了,所以sub2继续循环然后又进入wait

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值