Java多线程(四)线程间协作

本文深入探讨Java多线程中Wait与Notify机制,通过实例解析如何正确使用这两个方法来实现线程间的同步与资源抢占。文章指出在条件判断中使用While循环而非If语句的重要性,确保线程在被唤醒后重新检查条件,避免虚假唤醒问题。

等待与通知: wait/notify

多线程编程中,如果某线程执行的条件没有满足,可以先将这个线程暂停,等到其所需要的条件满足了再将其唤醒。伪代码如下:

1
2
3
4
5
6
7
atomic{
    while(保护条件不成立){
        暂停当前线程;
    }
    //执行目标动作
    doAction();
}

判断+执行 应该具有原子性。条件未满足而暂停被称为等待。一个线程更新了系统的状态,使得其他线程所需的条件得以满足的时候唤醒哪些被暂停的线程的过程叫做通知(Notify)。

在Java中,Object中有两个方法wait() notify()分别表示等待和通知,作用对象为当前线程。

看一个例子
Main.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
    public static void main(String[] args) {
        Gainer gainer = new Gainer();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    gainer.gainSource();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        gainer.addSource(a);

    }

}

Gainner.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Gainer {
    public static int sources = 1;

    public synchronized boolean gainSource() throws InterruptedException {
        if (sources <= 0){
            System.out.println(Thread.currentThread().getName() + "没有抢到资源");
            wait();
        }
        sources--;
        System.out.println(Thread.currentThread().getName() + "抢到了资源");
        return true;
    }

    public synchronized void addSource(int sourceCount){
        sources += sourceCount;
        this.notifyAll();
    }
}

这个代码测试的是多线程的资源抢占,初始资源数为1,然后通过输入流加3,运行结果如下:

第一部分的结果的符合预期的,然后我通过输入3使资源数+3,随后唤醒所有等待线程竞争,之后的结果就不对了,显示所有的线程都抢到了资源。

问题在哪呢,一个很细节的地方,在条件判断那里我写了if (sources <= 0){,线程第一次进来,如果没有可用资源,遇到wait方法,等待,如果未来的某个时间点该等待线程被唤醒了,那会继续从该处就是wait方法执行下去,那么如果用if,只会判断一次,所以在唤醒之后的那次就没有条件判断了,所以每个等待线程都抢到了资源。
需要将if改为while,并将wait放在while中的最后,这样,每当等待线程被唤醒的时候,直接回到条件判断。

另外注意main方法中,我在循环体外面定义了Gainer,刚开始的时侯尝试在线程体里面new,但这样发现同步方法没有用,因为非静态同步方法的锁对象是当前对象,所以如果在线程体里面new出来Gainer,那么就有10个对象10个锁,每个线程各占用一个锁,就不对了,一定要保证全局只有一把锁,另一种是可以使用静态变量作为锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值