zoukankan      html  css  js  c++  java
  • wait()与notify()

    一,前言

    ​ 简单画了一下线程的流程图,只是一个大概。如图所示,线程有多种状态,那么不同状态之间是如何切换的,下面主要总结关于wait()和notify()的使用。

    二,wait()

    ​ wait()和notify()都是定义在Object类中,为什么如此设计。因为synchronized中的这把锁可以是任意对象,所以任意对象都可以调用wait()和notify(),并且只有同一把锁才能对线程进行操作,不同锁之间是不可以相互操作的,所以wait和notify属于Object。请看如下API文档说明。

    ​ wait()提供三种构造方法,但前两种最为常用,wait()是让线程一直处于等待状态,直到手动唤醒,而wait(long timeout)可以指定等待时间,之后会自动唤醒。

    ​ 调用wait方法可以让当前线程进入等待唤醒状态,该线程会处于等待唤醒状态直到另一个线程调用了object对象的notify方法或者notifyAll方法。

    三,notify()

    ​ notify()唤醒等待的线程,如果监视器种只有一个等待线程,使用notify()可以唤醒。但是如果有多条线程notify()是随机唤醒其中一条线程,与之对应的就是notifyAll()就是唤醒所有等待的线程,请看下面实例代码。

    ​ 案例:定义两条线程,分别让其线程等待,及线程唤醒。

    ​ 1,定义线程。

    public class SetTarget implements Runnable{
    	private Demo demo;
    	public SetTarget(Demo demo) {
    		this.demo = demo;
    	}
    	@Override
    	public void run() {
    		demo.set();
    	}
    }
    
    public class GetTarget implements Runnable {
    	private Demo demo;
    	public GetTarget(Demo demo) {
    		this.demo = demo;
    	}
    	@Override
    	public void run() {
    		demo.get();
    	}
    }
    

    ​ 2,编写main方法。

    public class Demo {
    
    	// 定义一个信号量
    	private volatile int signal;
    
    	public static void main(String[] args) {
    		
    		Demo demo = new Demo();
    		SetTarget set = new SetTarget(demo);
    		GetTarget get = new GetTarget(demo);
    		
    		// 开启线程,
    		new Thread(get).start();
    		new Thread(get).start();
    		new Thread(get).start();
    		new Thread(get).start();
    		
    		try {
    			TimeUnit.SECONDS.sleep(1);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		new Thread(set).start();
    	}
    
    	// set方法唤醒线程
    	public synchronized void set() {
    		signal = 1;
    		// notify方法会随机叫醒一个处于wait状态的线程
    		notify(); 
    		// notifyAll叫醒所有的处于wait线程,争夺到时间片的线程只有一个
    		//notifyAll();
    		System.out.println("叫醒线程叫醒之后休眠开始...");
    		try {
    			Thread.sleep(3000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    	// get方法使线程进入等待状态
    	public synchronized int get() {
    		System.out.println(Thread.currentThread().getName() + " 方法执行了...");
    		if (signal != 1) {
    			try {
    				wait();
    				System.out.println("叫醒之后");
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		System.out.println(Thread.currentThread().getName() + " 方法执行完毕...");
    		return signal;
    	}
    }
    
    

    ​ 3,运行结果。

    ​ 分析:一共开启了4个线程,当全部进入等待状态时,调用notify()方法唤醒线程,但很明显只唤醒了其中一条线程。右上角显示程序并没有停止,原因就是其他3条线程仍在处于等待状态。

    ​ 使用notifyAll()唤醒线程:

    四,生产者-消费者模式

    ​ 生产者-消费者模式,生产者生产商品,然后通知消费者进行消费。

    ​ 1,定义生产者

    public class Vendor {
    	// 定义库存数量
    	private int count;
    
    	// 定义最大库存
    	private final int MAX_COUNT = 10;
    
    	public synchronized void production() {
    		while (count >= MAX_COUNT) {
    			try {
    				System.out.println(Thread.currentThread().getName() + "库存数量达到最大值,停止生产。");
    				// 此时生产线程全部进入等待状态
    				wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		// 否则生产商品
    		count++;
    		System.out.println(Thread.currentThread().getName() + "正在生产商品,当前库存为:" + count);
    		notifyAll();
    
    	}
    
    	public synchronized void consumers() {
    		while (count <= 0) {
    			try {
    				System.out.println(Thread.currentThread().getName() + "没有商品了,消费者处于等待状态...");
    				wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		count--;
    		System.out.println(Thread.currentThread().getName() + "正在消费,当前库存为:" + count);
    		notifyAll();
    	}
    }
    
    

    ​ 2,分别定义两条线程。

    public class SetTarget implements Runnable {
    
    	private Vendor vendor;
    
    	public SetTarget(Vendor vendor) {
    		this.vendor = vendor;
    	}
    
    	@Override
    	public void run() {
    		while(true){
    			vendor.production();
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    
    	}
    }
    
    public class GetTarget implements Runnable {
    
    	private Vendor vendor;
    
    	public GetTarget(Vendor vendor) {
    		this.vendor = vendor;
    	}
    
    	@Override
    	public void run() {
    		while(true){
    			vendor.consumers();
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    ​ 3,主方法。

    public class Demo {
    	public static void main(String[] args) {
    		Vendor vendor = new Vendor();
    		SetTarget set = new SetTarget(vendor);
    		GetTarget get = new GetTarget(vendor);
    		
    		// 开启线程生产商品
    		new Thread(set).start();
    		new Thread(set).start();
    		new Thread(set).start();
    		new Thread(set).start();
    		
    		// 开启消费者线程
    		new Thread(get).start();
    	}
    }
    

    ​ 4,运行结果。

    五,总结

    ​ 线程之间通信就做这么一个简单的总结,以上内容如有错误,欢迎留言指正。

    感谢阅读!

  • 相关阅读:
    linux挂载windows共享文件夹
    Cython
    python并行编程
    数据库学习----MySQL 存储引擎
    数据库学习----MySQL 日志
    数据库学习----从文件l数据到数据库
    Golang 学习 ---- 编译打包
    数字转换成千字符
    el-select选择框宽度与输入框相同
    git常用命令总结
  • 原文地址:https://www.cnblogs.com/fenjyang/p/11603229.html
Copyright © 2011-2022 走看看