Java多线程8:wait()和notify()/notifyAll()

轮询

线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作。

想像一个场景,A线程做int型变量i的累加操作,B线程等待i到了10000就打印出i,怎么处理?一个办法就是,B线程while(i == 10000),这样两个线程之间就有了通信,B线程不断通过轮训来检测i == 10000这个条件。

这样可以实现我们的需求,但是也带来了问题:CPU把资源浪费了B线程的轮询操作上,因为while操作并不释放CPU资源,导致了CPU会一直在这个线程中做判断操作。如果可以把这些轮询的时间释放出来,给别的线程用,就好了。

 

wait/notify

在Object对象中有三个方法wait()、notify()、notifyAll(),既然是Object中的方法,那每个对象自然都是有的。如果不接触多线程的话,这两个方法是不太常见的。下面看一下前两个方法:

1、wait()

wait()的作用是使当前执行代码的线程进行等待,将当前线程置入"预执行队列"中,并且wait()所在的代码处停止执行,直到接到通知或被中断。在调用wait()之前,线程必须获得该对象的锁,因此只能在同步方法/同步代码块中调用wait()方法。

2、notify()

notify()的作用是,如果有多个线程等待,那么线程规划器随机挑选出一个wait的线程,对其发出通知notify(),并使它等待获取该对象的对象锁。注意"等待获取该对象的对象锁",这意味着,即使收到了通知,wait的线程也不会马上获取对象锁,必须等待notify()方法的线程释放锁才可以。和wait()一样,notify()也要在同步方法/同步代码块中调用。

总结起来就是,wait()使线程停止运行,notify()使停止运行的线程继续运行。

 

wait()/notify()使用示例

看一段代码:

复制代码

public class MyThread30_0 extends Thread
{
    private Object lock;
    
    public MyThread30_0(Object lock)
    {
        this.lock = lock;
    }
    
    public void run()
    {
        try
        {
            synchronized (lock)
            {
                System.out.println("开始------wait time = " + System.currentTimeMillis());
                lock.wait();
                System.out.println("结束------wait time = " + System.currentTimeMillis());
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

复制代码

复制代码

public class MyThread30_1 extends Thread
{
    private Object lock;
    
    public MyThread30_1(Object lock)
    {
        this.lock = lock;
    }
    
    public void run()
    {
        synchronized (lock)
        {
            System.out.println("开始------notify time = " + System.currentTimeMillis());
            lock.notify();
            System.out.println("结束------notify time = " + System.currentTimeMillis());
        }
    }
}

复制代码

写个main函数,同样的Thread.sleep(3000)也是为了保证mt0先运行,这样才能看到wait()和notify()的效果:

复制代码

public static void main(String[] args) throws Exception
{
    Object lock = new Object();
    MyThread30_0 mt0 = new MyThread30_0(lock);
    mt0.start();
    Thread.sleep(3000);
    MyThread30_1 mt1 = new MyThread30_1(lock);
    mt1.start();
}

复制代码

看一下运行结果:

开始------wait time = 1443931599021
开始------notify time = 1443931602024
结束------notify time = 1443931602024
结束------wait time = 1443931602024

第一行和第二行之间的time减一下很明显就是3s,说明wait()之后代码一直暂停,notify()之后代码才开始运行。

wait()方法可以使调用该线程的方法释放共享资源的锁,然后从运行状态退出,进入等待队列,直到再次被唤醒。

notify()方法可以随机唤醒等待队列中等待同一共享资源的一个线程,并使得该线程退出等待状态,进入可运行状态

notifyAll()方法可以使所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入可运行状态 

最后,如果wait()方法和notify()/notifyAll()方法不在同步方法/同步代码块中被调用,那么虚拟机会抛出java.lang.IllegalMonitorStateException,注意一下。

 

wait()释放锁以及notify()不释放锁

多线程的学习中,任何地方都要关注"锁",wait()和notify()也是这样。wait()方法是释放锁的,写一个例子来证明一下:

复制代码

public class ThreadDomain31
{
    public void testMethod(Object lock)
    {
        try
        {
            synchronized (lock)
            {
                System.out.println(Thread.currentThread().getName() + " Begin wait()");
                lock.wait();
                System.out.println(Thread.currentThread().getName() + " End wait");
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

复制代码

复制代码

public class MyThread31 extends Thread
{
    private Object lock;
    
    public MyThread31(Object lock)
    {
        this.lock = lock;
    }
    
    public void run()
    {
        ThreadDomain31 td = new ThreadDomain31();
        td.testMethod(lock);
    }
}

复制代码

main函数调用一下:

复制代码

public static void main(String[] args)
{
    Object lock = new Object();
    MyThread31 mt0 = new MyThread31(lock);
    MyThread31 mt1 = new MyThread31(lock);
    mt0.start();
    mt1.start();
}

复制代码

看一下运行结果:

Thread-0 Begin wait()
Thread-1 Begin wait()

如果wait()方法不释放锁,那么Thread-1根本不会进入同步代码块打印的,所以,证明完毕。

接下来证明一下notify()方法不释放锁的结论:

复制代码

public class ThreadDomain32
{
    public void testMethod(Object lock)
    {
        try
        {
            synchronized (lock)
            {
                System.out.println("Begin wait(), ThreadName = " + Thread.currentThread().getName());
                lock.wait();
                System.out.println("End wait(), ThreadName = " + Thread.currentThread().getName());
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
    
    public void synNotifyMethod(Object lock)
    {
        try
        {
            synchronized (lock)
            {
                System.out.println("Begin notify(), ThreadName = " + Thread.currentThread().getName());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("End notify(), ThreadName = " + Thread.currentThread().getName());
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

复制代码

写两个线程分别调用2个方法:

复制代码

public class MyThread32_0 extends Thread
{
    private Object lock;
    
    public MyThread32_0(Object lock)
    {
        this.lock = lock;
    }
    
    public void run()
    {
        ThreadDomain32 td = new ThreadDomain32();
        td.testMethod(lock);
    }
}

复制代码

复制代码

public class MyThread32_1 extends Thread
{
    private Object lock;
    
    public MyThread32_1(Object lock)
    {
        this.lock = lock;
    }
    
    public void run()
    {
        ThreadDomain32 td = new ThreadDomain32();
        td.synNotifyMethod(lock);
    }
}

复制代码

写个main函数调用一下:

复制代码

public static void main(String[] args) throws Exception
{
    Object lock = new Object();
    MyThread32_0 mt0 = new MyThread32_0(lock);
    mt0.start();
    MyThread32_1 mt1 = new MyThread32_1(lock);
    mt1.start();
    MyThread32_1 mt2 = new MyThread32_1(lock);
    mt2.start();
}

复制代码

看一下运行结果:

Begin wait(), ThreadName = Thread-0
Begin notify(), ThreadName = Thread-1
End notify(), ThreadName = Thread-1
Begin notify(), ThreadName = Thread-2
End notify(), ThreadName = Thread-2
End wait(), ThreadName = Thread-0

如果notify()方法释放锁,那么在Thread-1调用notify()方法后Thread.sleep(5000)必定应该有其他线程可以进入同步代码块了,但是实际上没有,必须等到Thread-1把代码执行完。所以,证明完毕。

 

interrupt()打断wait()

之前有说过,interrupt()方法的作用不是中断线程,而是在线程阻塞的时候给线程一个中断标识,表示该线程中断。wait()就是"阻塞的一种场景",看一下用interrupt()打断wait()的例子:

复制代码

public class ThreadDomain33
{
    public void testMethod(Object lock)
    {
        try
        {
            synchronized (lock)
            {
                System.out.println("Begin wait()");
                lock.wait();
                System.out.println("End wait()");
            }
        }
        catch (InterruptedException e)
        {
            System.out.println("wait()被interrupt()打断了!");
            e.printStackTrace();
        }
    }
}

复制代码

复制代码

public class MyThread33 extends Thread
{
    private Object lock;
    
    public MyThread33(Object lock)
    {
        this.lock = lock;
    }
    
    public void run()
    {
        ThreadDomain33 td = new ThreadDomain33();
        td.testMethod(lock);
    }
}

复制代码

复制代码

public static void main(String[] args) throws Exception
{
    Object lock = new Object();
    MyThread33 mt = new MyThread33(lock);
    mt.start();
    Thread.sleep(5000);
    mt.interrupt();
}

复制代码

看一下运行结果:

复制代码

Begin wait()
wait()被interrupt()打断了!
java.lang.InterruptedException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:485)
    at com.xrq.example.e33.ThreadDomain33.testMethod(ThreadDomain33.java:12)
    at com.xrq.example.e33.MyThread33.run(MyThread33.java:15)

复制代码

 

notifyAll()唤醒所有线程

利用Object对象的notifyAll()方法可以唤醒处于同一监视器下的所有处于wait的线程,举个例子证明一下:

复制代码

public class ThreadDomain34
{
    public void testMethod(Object lock)
    {
        try
        {
            synchronized (lock)
            {
                System.out.println("Begin wait(), ThreadName = " + Thread.currentThread().getName());
                lock.wait();
                System.out.println("End wait(), ThreadName = " + Thread.currentThread().getName());
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

复制代码

写两个线程,一个调用testMethod(Object lock)的线程,一个notifyAll()线程:

复制代码

public class MyThread34_0 extends Thread
{
    private Object lock;
    
    public MyThread34_0(Object lock)
    {
        this.lock = lock;
    }
    
    public void run()
    {
        ThreadDomain34 td = new ThreadDomain34();
        td.testMethod(lock);
    }
}

复制代码

复制代码

public class MyThread34_1 extends Thread
{
    private Object lock;
    
    public MyThread34_1(Object lock)
    {
        this.lock = lock;
    }
    
    public void run()
    {
        synchronized (lock)
        {
            lock.notifyAll();
        }
    }
}

复制代码

main函数开三个wait线程,用一个notifyAll的线程去唤醒:

复制代码

public static void main(String[] args) throws Exception
{
    Object lock = new Object();
    MyThread34_0 mt0 = new MyThread34_0(lock);
    MyThread34_0 mt1 = new MyThread34_0(lock);
    MyThread34_0 mt2 = new MyThread34_0(lock);
    mt0.start();
    mt1.start();
    mt2.start();
    Thread.sleep(1000);
    MyThread34_1 mt3 = new MyThread34_1(lock);
    mt3.start();
}

复制代码

看一下运行结果:

Begin wait(), ThreadName = Thread-0
Begin wait(), ThreadName = Thread-2
Begin wait(), ThreadName = Thread-1
End wait(), ThreadName = Thread-1
End wait(), ThreadName = Thread-2
End wait(), ThreadName = Thread-0

当然,唤醒的顺序不重要,因为notifyAll()把处于同一资源下wait的线程全部唤醒,至于唤醒的顺序,就和线程启动的顺序一样,是虚拟机随机的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
自动控制节水灌溉技术的高低代表着农业现代化的发展状况,灌溉系统自动化水平较低是制约我国高效农业发展的主要原因。本文就此问题研究了单片机控制的滴灌节水灌溉系统,该系统可对不同土壤的湿度进行监控,并按照作物对土壤湿度的要求进行适时、适量灌水,其核心是单片机和PC机构成的控制部分,主要对土壤湿度与灌水量之间的关系、灌溉控制技术及设备系统的硬件、软件编程各个部分进行了深入的研究。 单片机控制部分采用上下位机的形式。下位机硬件部分选用AT89C51单片机为核心,主要由土壤湿度传感器,信号处理电路,显示电路,输出控制电路,故障报警电路等组成,软件选用汇编语言编程。上位机选用586型以上PC机,通过MAX232芯片实现同下位机的电平转换功能,上下位机之间通过串行通信方式进行数据的双向传输,软件选用VB高级编程语言以建立友好的人机界面。系统主要具有以下功能:可在PC机提供的人机对话界面上设置作物要求的土壤湿度相关参数;单片机可将土壤湿度传感器检测到的土壤湿度模拟量转换成数字量,显示于LED显示器上,同时单片机可采用串行通信方式将此湿度值传输到PC机上;PC机通过其内设程序计算出所需的灌水量和灌水时间,且显示于界面上,并将有关的灌水信息反馈给单片机,若需灌水,则单片机系统启动鸣音报警,发出灌水信号,并经放大驱动设备,开启电磁阀进行倒计时定时灌水,若不需灌水,即PC机上显示的灌水量和灌水时间均为0,系统不进行灌水。
智慧农业是一种结合了现代信息技术,包括物联网、大数据、云计算等,对农业生产过程进行智能化管理和监控的新模式。它通过各种传感器和设备采集农业生产中的关键数据,如大气、土壤和水质参数,以及生物生长状态等,实现远程诊断和精准调控。智慧农业的核心价值在于提高农业生产效率,保障食品安全,实现资源的可持续利用,并为农业产业的转型升级提供支持。 智慧农业的实现依赖于多个子系统,包括但不限于设施蔬菜精细化种植管理系统、农业技术资料库、数据采集系统、防伪防串货系统、食品安全与质量追溯系统、应急追溯系统、灾情疫情防控系统、农业工作管理系统、远程诊断系统、监控中心、环境监测系统、智能环境控制系统等。这些系统共同构成了一个综合的信息管理和服务平台,使得农业生产者能够基于数据做出更加科学的决策。 数据采集是智慧农业的基础。通过手工录入、传感器自动采集、移动端录入、条码/RFID扫描录入、拍照录入以及GPS和遥感技术等多种方式,智慧农业系统能够全面收集农业生产过程中的各种数据。这些数据不仅包括环境参数,还涵盖了生长状态、加工保存、检验检疫等环节,为农业生产提供了全面的数据支持。 智慧农业的应用前景广阔,它不仅能够提升农业生产的管理水平,还能够通过各种应用系统,如库房管理、无公害监控、物资管理、成本控制等,为农业生产者提供全面的服务。此外,智慧农业还能够支持政府监管,通过发病报告、投入品报告、死亡报告等,加强农业产品的安全管理和质量控制。 面对智慧农业的建设和发展,存在一些挑战,如投资成本高、生产过程标准化难度大、数据采集和监测的技术难题等。为了克服这些挑战,需要政府、企业和相关机构的共同努力,通过政策支持、技术创新和教育培训等手段,推动智慧农业的健康发展。智慧农业的建设需要明确建设目的,选择合适的系统模块,并制定合理的设备布署方案,以实现农业生产的智能化、精准化和高效化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值