Java多线程之线程虚假唤醒

本文探讨了Java多线程环境下线程虚假唤醒的问题,通过对比不同线程数量下对共享变量的操作,揭示了使用wait和notify时可能出现的问题。并介绍了如何使用while循环和Lock与Condition优化线程同步,避免虚假唤醒。

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

Java多线程之线程虚假唤醒


本文目录提纲

  1. 问题:两个线程对一个初始值为零的变量操作,实现一个线程加一,另一个线程减一,来十次。
  2. 问题:四个线程对一个初始值为零的变量操作,实现两个线程加一,另外两个线程减一,来十次。

1. 两个线程对一个初始值为零的变量操作,实现一个线程加一,另一个线程减一,来十次。

代码实现:

class ShareData{
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
//        判断
        if (number!=0){
            this.wait();
        }
//        干活
        ++number;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
//        通知唤醒
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            this.wait();
        }
        --number;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }
}

public class ThreadDemo2 {
    public static void main(String[] args) {
     ShareData sd = new ShareData();

     new Thread(()->{
         for (int i = 1; i <=10 ; i++) {
             try {
                 sd.increment();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }  
     },"A").start();

        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}

编译结果:
在这里插入图片描述

多次测试,是没有问题的。

2. 问题:四个线程对一个初始值为零的变量操作,实现两个线程加一,另外两个线程减一,来十次。

于是把上面的代码,线程弄多两个,运行发现出现问题。编译结果如下:
在这里插入图片描述


通过阅读文档,发现:

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }
即中断和伪唤醒是可能的,并且这个方法应该在while循环中使用

画图:

在这里插入图片描述

将上面代码if换成while再执行,发现没有问题,代码如下:

class ShareData {
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
//        判断
        while (number != 0) {
            this.wait();
        }
//        干活
        ++number;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
//        通知唤醒
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while (number == 0) {
            this.wait();
        }
        --number;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }
}

public class ThreadDemo2 {
    public static void main(String[] args) {
        ShareData sd = new ShareData();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(200);
                    sd.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(300);     //模拟执行其他代码
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(400);
                    sd.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(500);
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

编译结果:
在这里插入图片描述


使用Lock优化

前置知识:

Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use ofsynchronized methods and statements, a Condition replaces the use of the Object monitor methods.


优化代码:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ShareData {
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public synchronized void increment() throws InterruptedException{
        lock.lock();
        try{
            while (number != 0) {
//                this.wait();
                condition.await();
            }
//        干活
            ++number;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
//        通知唤醒
//            this.notifyAll();
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public synchronized void decrement() throws InterruptedException{
        lock.lock();
        try{
            while (number == 0) {
//                this.wait();
                condition.await();
            }
//        干活
            --number;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
//        通知唤醒
//            this.notifyAll();
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

public class ThreadDemo2 {
    public static void main(String[] args) {
        ShareData sd = new ShareData();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(200);
                    sd.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(300);
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(400);
                    sd.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(500);
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值