Java线程之间的通信-等待/通知机制

线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能形成一个有效的整体,线程间的通讯就是称为一个整体的必用方案,

等待/通知机制的实现
  方法wait()的作用是是当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程置入”预执行队列”中,并且在wait()所处的代码停止执行,知道接到通知或被中断。在调用wait()之前,线程必须获得该对象的对象级别锁。即只能在同步方法或者同步代码块中调用wait()方法。在执行wait()方法后,释放该锁。如果调用wait()方法没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类。不需要执行try catch语句。
  方法notify()同样要在同步的方法或者代码块中执行,必须持有对象级别的锁,如果没有拿到锁则会抛出IllegalMonitorStateException。该方法是用来通知那些可能等待该对象的对象所的其他线程。
  在执行notify()方法后,并不会马上释放该锁,要等到执行notiy()方法的线程将程序执行完毕。也就是退出synchronized代码块后,才会释放锁。
  合理的情况下wait()和notify()必须成对出来,因为当一个wait线程运行完毕后,它会释放掉该对象锁,如果该对象没有使用notify()方法会导致其余处在wait()状态的线程将无法获取该锁。

errorCode

public class Demo
{
    public static void main(String[] args)
    {
        try
        {
            String str = "aaaa";
            str.wait();
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}
这里写代码片

  程序将会抛出IllegalMonitorStateException异常,因为没有获取到锁

   关键字synchronized可以将任何一个Object对象作为同步对象来看待,而Java为每个Object都实现了notify()方法和wait()方法。他们必须在synchronized同步代码块中执行,通过调用wait()操作,而处于临界区内的线程进入等待状态,同时释放该对象的锁,而notify操作可以唤醒一个因调用wait操作而处于阻塞状态中的线程进入就绪状态。被重新唤醒的线程会试图重新获得临界区的控制权,也就是锁,并继续执行wait()方法后的代码。如果notify方法调用时候,没有线程处于阻塞状态就会忽视该代码
  wait()方法可以使一个线程释放掉共享资源锁,然后从运行状态中退出。进入到等待对待。知道再次被唤醒。
  notify()方法可以随机唤醒等待队列中的等待统一共享资源的“一个”线程。并是该线程退出等待队列。进入可运行状态。也就是notify()方法仅通知“一个”线程。
  notifyAll()方法可以使所有正在等待队列中等待同一个共享资源的“全部”,线程从等待状态退出。进入可运行状态。此时优先级最高的先执行。哪个线程先执行,取决于JVM虚拟机的实现。
  分析线程的状态:

  创建一个新的线程对象后,调用start()方法后,线程处在一个runnable,runnable是一个可运行的状态,当线程获取CPU时间片的时候,线程处在running状态。
  running状态和runnable状态可以互相切换。当CPU时间片执行完毕后,线程从running回到runnable状态。

  线程进入runnable状态有五种情况:

  • 调用sleep方法,经过指定的时间后
  • 线程调用的阻塞IO已经返回,阻塞方法执行完毕
  • 线程成功地获取试图同步的监视器
  • 线程正在等待,收到通知
  • 处于挂起状态的线程,调用resume方法

  Block是阻塞状态,Block状态结束后,进入到runnable状态。
- 调用sleep方法,主动放弃占用的处理资源
- 线程调用了阻塞式的IO方法,该方法返回前,线程处于阻塞状态
- 试图获得到一个同步监视器,但是监视器被其他线程所占有
- 等待通知状态
- 调用了suspend
4、run()方法运行结束后进入销毁,整个线程执行完毕

  每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪对象存储了将要获得锁的线程,阻塞队列存储了被阻塞状态的线程,一个线程被唤醒之后进入就绪队列。等待CPU的调度,反之,一个线程被wait后,进入阻塞状态。

  wait()方法调用会立即释放锁,notify()方法被调用锁不会立即释放。notify()方法所在的同步synchronized代码块后才释放该锁。
  有三种情况会释放掉所持有的锁对象

  1. 运行完synchronized代码块,和方法会释放掉该锁
  2. 抛出异常会释放掉该锁
  3. 执行到wait()方法会释放掉锁

  当线程处在wait()方法时,调用interrupt会抛出InterruptedException异常
  我们在调用notify()一次只会随机通知一个线程进行唤醒,而notifyAll()会唤醒全部的线程。
  wait(long millions)等待毫秒数,当线程在等待的时间内如果遇到notify(),线程将会唤醒,或者等待时间到了,线程也会被唤醒。

  来看一个经典的例子生产者和消费者,其中运用到的是等待/通知,进行多线程通讯

package com.tony.ameng;

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

public class MyStack
{
    private List list = new ArrayList<>();

    synchronized public void push()
    {
        try
        {
            while (list.size() >= 50)
            {
                System.out.println("当仓库中的商品多余50件的时候停止生产!!");
                this.wait();
            }
            list.add("anyString = " + Math.random());
            this.notifyAll();//通知消费者线程进行消费
            System.out.println("push = " + list.size());
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

    synchronized public String pop()
    {
        String returnString = "";
        try
        {
            while(list.size() == 0)
            {
                System.out.println("pop操作中的:" + 
                            Thread.currentThread().getName() + "线程处在wait状态");
                this.wait();
            }

            returnString = list.get(0).toString();
            list.remove(0);
            this.notifyAll();
            System.out.println("pop = " + list.size());
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        return returnString;
    }

}

仓库,有两个方法一个生产,一个是消费

package com.tony.ameng;

public class Consumer
{
    private MyStack mStack;

    public Consumer(MyStack mStack)
    {
        this.mStack = mStack;
    }

    public void popService()
    {
        System.out.println("pop=" + mStack.pop());
    }
}

消费者

package com.tony.ameng;

public class CThread extends Thread
{
    private Consumer mConsumer;

    public CThread(Consumer mConsumer)
    {
        this.mConsumer = mConsumer;
    }

    @Override
    public void run()
    {
        while(true)
        {
            mConsumer.popService();
        }
    }
}

消费者线程

package com.tony.ameng;

public class Producer
{
    private MyStack mStack;

    public Producer(MyStack myStack)
    {
        this.mStack = myStack;
    }

    public void pushService()
    {
        mStack.push();
    }
}

消费者

package com.tony.ameng;

public class PThread extends Thread
{
    private Producer mProducer;

    public PThread(Producer mProducer)
    {
        super();
        this.mProducer = mProducer;
    }

    @Override
    public void run()
    {
        while(true)
        {
            mProducer.pushService();
        }
    }

}

生产者线程

package com.tony.ameng;

public class Run
{
    public static void main(String[] args)
    {
        MyStack mStack = new MyStack();
        Producer mProducer = new Producer(mStack);
        Producer mProducer2 = new Producer(mStack);
        Producer mProducer3 = new Producer(mStack);
        Producer mProducer4 = new Producer(mStack);

        Consumer mConsumer = new Consumer(mStack);
        Consumer mConsumer2 = new Consumer(mStack);
        Consumer mConsumer3 = new Consumer(mStack);
        Consumer mConsumer4 = new Consumer(mStack);

        PThread pThread = new PThread(mProducer);
        PThread pThread2 = new PThread(mProducer2);
        PThread pThread3 = new PThread(mProducer3);
        PThread pThread4 = new PThread(mProducer4);


        CThread cThread = new CThread(mConsumer);
        CThread cThread2 = new CThread(mConsumer2);
        CThread cThread3 = new CThread(mConsumer3);
        CThread cThread4 = new CThread(mConsumer4);

        pThread.start();
        pThread2.start();
        pThread3.start();
        pThread4.start();
        cThread.start();
        cThread2.start();
        cThread3.start();
        cThread4.start();
    }
}

main函数,多个消费者与生产者的模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值