多线程(三)

目录

一. 生产消费者模型分析

1. wait()

2. notify()

3. notifyAll()

4. 小结

二. 生产消费者模型实例

1. 调用notify()唤醒线程

2. 调用notifyAll()唤醒线程


 

 

一. 生产消费者模型分析


1. wait()

wait()方法会使线程停止运行,会释放对象锁。(状态变化:运行状态--->阻塞状态)

用于同步代码块或同步方法,并且必须是内建锁。

特点:

  • wait()方法会使当前线程调用该方法后进行等待,并且将该线程置入锁对象的等待队列中,直到接到通知或被中断为止。
  • wait()方法只能在同步方法或同步代码块中调用,如果调用wait()时没有适当的锁,会抛出异常。
  • wait()方法执行后,当前线程释放锁,其他线程可以竞争该锁。

wait()(无参)之后的线程继续执行有两种方法:

  1. 调用该对象的notify()唤醒该线程。
  2. 线程等待时调用interrupt()中断该线程。

调用notify():调用notify()唤醒线程在讲述完notify()之后再举例说明。

调用interrupt():

package www.like.java;


class MyThread implements Runnable{
    private Object obj = new Object();

    @Override
    public void run() {
       synchronized (obj){
           System.out.println("wait方法开始。。。");
           try {
               obj.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("wait方法结束。。。");
       }
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread);
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }
}

运行结果:产生中断异常,然后结束。

 

wait()还有一个有参构造:

参数timeout指的是等待的时间(精确到毫秒),如果到了预计时间还没有被唤醒,线程就结束等待,继续执行。

 

2. notify()

notify()方法会唤醒正在等待的线程,就是使停止的线程继续运行。

特点:

  • notify()方法也必须在同步方法或同步代码块中调用,用来唤醒等待在该对象上的线程。如果有多个线程等待,就任意挑选一个线程唤醒。
  • notify()方法执行后,唤醒线程不会立刻释放对象锁,要等待唤醒线程执行完毕后才释放对象锁。
package www.like.java;


class MyThread implements Runnable{
    private Object obj = new Object();
    private boolean flag;

    public MyThread(Object obj, boolean flag) {
        this.obj = obj;
        this.flag = flag;
    }

    public void waitMethod(){
        synchronized (obj){
            System.out.println("wait方法开始。。。"+Thread.currentThread().getName());
            try {
                obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("wait方法结束。。。"+Thread.currentThread().getName());
        }
    }

    public void notifyMethod(){
        synchronized (obj){
            System.out.println("notify方法开始。。。"+Thread.currentThread().getName());
            obj.notify();
            System.out.println("notify方法结束。。。"+Thread.currentThread().getName());
        }
    }

    @Override
    public void run() {
        if(flag){
            waitMethod();
        }else {
            notifyMethod();
        }
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        MyThread myThread1 = new MyThread(object,true);
        MyThread myThread2 = new MyThread(object,false);
        Thread waitThread = new Thread(myThread1,"等待线程");
        Thread notifyThread = new Thread(myThread2,"唤醒线程");
        waitThread.start();
        Thread.sleep(1000);
        notifyThread.start();
    }
}

运行结果:

看一下结果,我们注意到wait()方法是在notify()方法结束之后才继续执行的,这也印证了notify()的一个特点,唤醒线程执行完毕之后才释放锁。只有等notify()方法释放锁后,wait()方法才能申请到锁,继续执行。如果wait()方法没有被唤醒,就一直处于等待状态中。

 

3. notifyAll()

顾名思义,就是唤醒所有在该对象上等待的线程。

 

4. 小结

4.1 线程阻塞的几种情况

状态变化:运行状态--->阻塞状态

  • 调用sleep()方法,主动放弃占有的CPU,不会释放锁。
  • 调用阻塞式I/O方法(read()、write()),在该方法返回前,线程阻塞。
  • 线程试图获取一个同步监视器,但该同步监视器被其它线程所持有导致阻塞。
  • 线程等待某个通知,即调用wait(),释放对象锁。
  • 调用线程suspend(),将线程挂起,但该方法容易导致死锁,已被废弃。

 

4.2 锁对象的两个队列

每个锁对象都有两个队列,一个称为同步队列,一个称为等待队列。

同步队列中存放了因为竞争monitor失败导致阻塞的线程,这些线程等待CPU调度再次竞争锁。

等待队列中存放因为调用wait()方法导致线程等待的线程,唤醒后进入同步队列竞争锁。

 

二. 生产消费者模型实例


1. 调用notify()唤醒线程

package www.like.java;

//商品类
class Goods{
    private String goodsName;
    private int count;

    //生产商品方法
    public synchronized void set(String goodsName){
        if(count > 0){
            System.out.println("商品还有库存,等会在忙~");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.goodsName = goodsName;
        this.count++;
        System.out.println(Thread.currentThread().getName()+"生产"+toString());
        //唤醒等待消费的线程(生产完商品后告诉消费者可以开始消费了)
        notify();
    }

    //消费商品方法
    public synchronized void get(){
        if (count == 0){
            System.out.println("商品卖完了,客官请等待~");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.count--;
        System.out.println(Thread.currentThread().getName()+"消费"+toString());
        //唤醒等待生产的线程(消费完商品后告诉生产者可以开始生产了)
        notify();
    }

    @Override
    public String toString() {
        return "商品名称:"+goodsName+" 个数:"+count;
    }
}

//消费者线程
class Consumer implements Runnable{
    private Goods goods;

    public Consumer(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        goods.get();
    }
}

//生产者线程
class Producer implements Runnable{

    private Goods goods;

    public Producer(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        goods.set("笔记本");
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        //一定要传同一个商品(对象),生产和消费是同一个产品
        Goods goods = new Goods();
        Thread producerThread = new Thread(new Producer(goods),"生产者");
        Thread consumerThread = new Thread(new Consumer(goods),"消费者");
        Thread.sleep(1000);
        consumerThread.start();
        producerThread.start();
    }
}

 

2. 调用notifyAll()唤醒线程

package www.like.java;

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

//商品类
class Goods{
    private String goodsName;
    private int count;

    //生产商品方法
    public synchronized void set(String goodsName){
        //对生产者不做限制,一直不停的生产
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.goodsName = goodsName;
        this.count++;
        System.out.println(Thread.currentThread().getName()+"生产"+toString());
        //唤醒等待消费的线程
        notifyAll();
    }

    //消费商品方法
    public synchronized void get(){
        //不断的执行判定条件
        while(count == 0){
            System.out.println("商品卖完了,客官请等等~");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.count--;
        System.out.println(Thread.currentThread().getName()+"消费"+toString());
        //唤醒生产者继续生产商品
        notifyAll();
    }

    @Override
    public String toString() {
        return "商品名称:"+goodsName+" 个数:"+count;
    }

    public int getCount() {
        return count;
    }
}

//消费者线程
class Consumer implements Runnable{

    private Goods goods;

    public Consumer(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        while (true){
            //不断消费
            goods.get();
        }
    }
}

//生产者线程
class Producer implements Runnable{

    private Goods goods;

    public Producer(Goods goods) {
        this.goods = goods;
    }

    @Override
    public void run() {
        while (this.goods.getCount() < 10){
            //不断生产
            goods.set("笔记本");
        }
    }
}

public class Test {
    public static void main(String[] args)  {
       Goods goods = new Goods();
       //存储多个消费者、生产者线程
        List<Thread> list = new ArrayList<>();
        //10个消费者线程
        for(int i = 0; i < 10; i++){
            Thread thread = new Thread(new Consumer(goods),"消费者"+i);
            list.add(thread);
        }
        //5个生产者线程
        for(int i = 0; i < 5; i++){
            Thread thread = new Thread(new Producer(goods),"生产者"+i);
            list.add(thread);
        }
        for(Thread thread:list){
            thread.start();
        }
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值