线程之间的通信(wait()/notify()机制)——等待唤醒机制

wait() : 使当前同步监视器上的线程进入等待状态, 同时释放锁     对象名.wait()
notify() / notifyAll() : 唤醒当前同步监视器上等待状态的一个(所有)线程    对象名.notify()
-------------------- 上述方法必须在加锁的状态下使用(使用在同步方法中)

wait(),notify();notifyAll()这些方法定义在object类原因是琐是任意对象,而这几个方法又必须用在锁当中

 

例1:使用两个线程,交替打印1-100

public class PrintRunnable implements Runnable {

	int i = 0;

	@Override
	public synchronized void run() {
		while (true) {
			notify();//唤醒等待的线程
			if (i < 100) {
				System.out.println(Thread.currentThread().getName() + ":" + i++);
			}
			try {
				wait();//等待 同时释放锁
			} catch (InterruptedException e) {
			}
		}
	}
}
public class MyTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//线程执行体 下面的线程传入同一个runnable
		PrintRunnable printRunnable = new PrintRunnable();

		//两个线程执行打印
		Thread print1 = new Thread(printRunnable, "打印者1");
		Thread print2 = new Thread(printRunnable, "打印者2");

		//开始打印
		print1.start();
		print2.start();
	}
}

例2:模拟售票程序,实现三个窗口同时卖100张票

public class Tickets implements Runnable {

	int tick = 100;
	Object object = new Object();

	@Override
	public void run() {
		while (true) {
			synchronized (object) {
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//将操作共享变量的代码块放入同步监视器里
				if (tick > 0) {
					tick--;
					System.out.println(Thread.currentThread().getName() + "完成售票,余票为 :" + tick);
				}
			}
		}
	}
}
public class MyTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//线程执行体 下面的几个线程 构造器传入同一个runnable 所以共享变量不需要static修饰也可以
		Tickets tickets = new Tickets();

		//三个窗口 三个线程
		Thread win1 = new Thread(tickets, "窗口1");
		Thread win2 = new Thread(tickets, "窗口2");
		Thread win3 = new Thread(tickets, "窗口3");

		//开始卖票
		win1.start();
		win2.start();
		win3.start();

	}
}

例3:生产者消费者问题

店员拥有产品属性

public class Clerk {
	//属性 店员的产品
	private int product;

	// 公共的get/set方法
	public int getProduct() {
		return product;
	}

	public void setProduct(int product) {
		this.product = product;
	}

	// 无参和全参构造器
	public Clerk() {
	}

	public Clerk(int product) {
		this.product = product;
	}

	// 生产商送货的方法
	public synchronized void getproducts() {
		while (product >= 20) {
                //如果用if (product >= 20)会有打印:"成功送来第32号货物" 等这些错误情况   这是有多个消费者和多个生产者的的时候存在虚假唤醒的问题
	        //解决办法 while (product >= 20)总是放在循环中,在获取cpu资源时会再去执行判断

			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			System.out.println("货物已满");
			try {
				wait();
				//wait()线程等待并且释放锁资源  多个生产者或者消费者的时候 有可能会有两个或者多个生产者再次等待
				//将if换成while 总是使用在循环中 当wait中的线程获取到cpu资源是时候会先去再重新判断while里的状态再执行  解决虚假唤醒
			} catch (InterruptedException e) {
			}
		}
		/**
		 * else{
		 	System.out.println(Thread.currentThread().getName() + "成功送来第" + ++product + "号货物");
			notifyAll();
		   }
		   如果用if else 此处notifyAll()可能执行不到  又无法被唤醒的问题
                   所以要保证 notifyAll() 必须呗执行,否则可能出现无法唤醒其他线程的问题
		 */
		System.out.println(Thread.currentThread().getName() + "成功送来第" + ++product + "号货物");
		notifyAll();
	}

	// 消费者取走货物的方法
	public synchronized void takeOffproduct() {
		while (product <= 0) {
			System.out.println("已缺货!!!");
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}
		System.out.println(Thread.currentThread().getName() + "成功取走第" + product-- + "号货物");
		notifyAll();  
		
	}
}

生产者

public class Productor implements Runnable{

	private Clerk clerk;

	// 公共的get/set方法
	public Clerk getClerk() {
		return clerk;
	}

	public void setClerk(Clerk clerk) {
		this.clerk = clerk;
	}

	// 无参和全参构造器
	public Productor() {
	}

	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}

	//实现接口的run方法执行体
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.getproducts();
		}
	}
}

消费者

public class Customer implements Runnable{

	private Clerk clerk;

	// 公共的get/set方法
	public Clerk getClerk() {
		return clerk;
	}

	public void setClerk(Clerk clerk) {
		this.clerk = clerk;
	}

	// 无参和全参构造器
	public Customer() {
	}

	public Customer(Clerk clerk) {
		this.clerk = clerk;
	}

	// 消费者取走货物的方法,实现接口run方法执行体
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.takeOffproduct();
		}
	}
}

test

public class MyTest {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//店员 拥有产品属性
		Clerk clerk = new Clerk();

		//生产方法
		Productor productor = new Productor(clerk);
		//消费方法
		Customer customer = new Customer(clerk);

		//两个生产者去生产
		new Thread(productor, "生产者").start();
		new Thread(productor, "生产者2").start();
		//两个消费者去消费
		new Thread(customer, "消费者").start();
		new Thread(customer, "消费者2").start();
	}

}

测试结果

消费者成功取走第20号货物
消费者成功取走第19号货物
消费者成功取走第18号货物
消费者成功取走第17号货物
消费者成功取走第16号货物
消费者成功取走第15号货物
消费者成功取走第14号货物
消费者成功取走第13号货物
消费者成功取走第12号货物
消费者成功取走第11号货物
消费者成功取走第10号货物
消费者成功取走第9号货物
消费者成功取走第8号货物
消费者成功取走第7号货物
消费者成功取走第6号货物
消费者成功取走第5号货物
消费者成功取走第4号货物
消费者成功取走第3号货物
消费者成功取走第2号货物
消费者成功取走第1号货物
已缺货!!!
生产者成功送来第1号货物
生产者成功送来第2号货物
生产者成功送来第3号货物
生产者成功送来第4号货物
生产者成功送来第5号货物
生产者成功送来第6号货物
生产者成功送来第7号货物
生产者成功送来第8号货物
生产者成功送来第9号货物
生产者成功送来第10号货物
生产者成功送来第11号货物
生产者成功送来第12号货物
生产者成功送来第13号货物
生产者成功送来第14号货物
生产者成功送来第15号货物
生产者成功送来第16号货物
生产者成功送来第17号货物
生产者成功送来第18号货物
生产者成功送来第19号货物
生产者成功送来第20号货物
货物已满
消费者成功取走第20号货物
消费者成功取走第19号货物
消费者成功取走第18号货物
消费者成功取走第17号货物
消费者成功取走第16号货物
消费者成功取走第15号货物
消费者成功取走第14号货物
消费者成功取走第13号货物
消费者成功取走第12号货物
消费者成功取走第11号货物
消费者成功取走第10号货物
消费者成功取走第9号货物
消费者成功取走第8号货物
消费者成功取走第7号货物
消费者成功取走第6号货物
消费者成功取走第5号货物
消费者成功取走第4号货物
消费者成功取走第3号货物
消费者成功取走第2号货物
消费者成功取走第1号货物
已缺货!!!
生产者成功送来第1号货物
生产者成功送来第2号货物
生产者成功送来第3号货物
生产者成功送来第4号货物
生产者成功送来第5号货物
生产者成功送来第6号货物
生产者成功送来第7号货物
生产者成功送来第8号货物
生产者成功送来第9号货物
生产者成功送来第10号货物
生产者成功送来第11号货物
生产者成功送来第12号货物
生产者成功送来第13号货物
生产者成功送来第14号货物
生产者成功送来第15号货物
生产者成功送来第16号货物
生产者成功送来第17号货物
生产者成功送来第18号货物
生产者成功送来第19号货物
生产者成功送来第20号货物
货物已满

用Lock实现锁以及等待唤醒机制来做消费者和生产者

/**
 * Created by wangxiaodong on 2019/8/6.
 * 用了Lock后  就不用synchronized了
 *
 * synchronized:等待唤醒机制:Object中的    : wait()、 notify() 和 notifyAll()
 * Lock:        等待唤醒机制:Condition中的 : await()、signal() 和 signalAll()
 */
public class Clerk {

	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();

	//属性 店员的产品
	private int product;

	// 公共的get/set方法
	public int getProduct() {
		return product;
	}

	public void setProduct(int product) {
		this.product = product;
	}

	// 无参和全参构造器
	public Clerk() {
	}

	public Clerk(int product) {
		this.product = product;
	}

	// 生产商送货的方法
	public  void getproducts() {
		lock.lock();
		try{
			while (product >= 20) { //如果用if (product >= 20)会有打印:"成功送来第32号货物" 等这些错误情况   这是有多个消费者和多个生产者的的时候存在虚假唤醒的问题
				//解决办法 while (product >= 20)

				try {
					Thread.sleep(20);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				System.out.println("货物已满");
				try {
					condition.await();
					//wait()线程等待并且释放锁资源  多个生产者或者消费者的时候 有可能会有两个或者多个生产者再次等待
					//将if换成while 总是使用在循环中 当wait中的线程获取到cpu资源是时候会先去再重新判断while里的状态再执行  解决虚假唤醒
				} catch (InterruptedException e) {
				}
			}
			/**
			 * else{
			 System.out.println(Thread.currentThread().getName() + "成功送来第" + ++product + "号货物");
			 notifyAll();
			 }
			 如果用if else此处可能执行不到  又无法被唤醒的问题
			 */
			System.out.println(Thread.currentThread().getName() + "成功送来第" + ++product + "号货物");
			condition.signalAll();
		}finally{
			lock.unlock();
		}
	}

	// 消费者取走货物的方法
	public synchronized void takeOffproduct() {
		lock.lock();
		try{
			while (product <= 0) {
				System.out.println("已缺货!!!");
				try {
					condition.await();
				} catch (InterruptedException e) {
				}
			}
			System.out.println(Thread.currentThread().getName() + "成功取走第" + product-- + "号货物");
			condition.signalAll();
		}finally{
			lock.unlock();
		}
	}
}

 

例4:现在两个线程,可以操作同一个变量,实现一个线程对该变量加1,一个线程对该变量减1, 实现交替,来十轮,变量初始值为0。

1 高内聚低耦合

2 线程  操作  资源

public class ShareData {
	private int num = 0;

	// 增加数据的方法
	public synchronized void incrment() throws InterruptedException {
		// 此处应该用While不能用if:因为多个线程被唤醒进入if时会出错误,会出现虚假唤醒,while可以拉回重新判断
		while (num != 0) {
			// 等待
			this.wait();
		}
		num++;
		System.out.println(Thread.currentThread().getName() + "\t" + num);
		// 唤醒其他所有线程
		this.notifyAll();
	}

	// 减少数据的方法
	public synchronized void descment() throws InterruptedException {
		while (num == 0) {
			// 等待
			this.wait();
		}
		num--;
		System.out.println(Thread.currentThread().getName() + "\t" + num);
		this.notifyAll();
	}
}
public class MyTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// 内部类如果要引用外部类的变量,则该变量必须为final,这是规定
		final ShareData sd = new ShareData();

		//线程1
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						sd.incrment();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "AA").start();

		//线程2
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						sd.descment();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "BB").start();

		//线程3
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						sd.incrment();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "CC").start();

		//线程4
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						sd.descment();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "DD").start();
	}
}

 总结:

1、wait() 要用在while判断中,否则会有的线程被虚假唤醒的情况

2、notifyAll()要保证被执行到,否则会有其他线程不被唤醒的情况

 

例5:编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要求输出的结果必须按顺序显示。如:ABCABCABC…… 依次递归

public class TestABCAlternate {
	
	public static void main(String[] args) {
		AlternateDemo ad = new AlternateDemo();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				
				for (int i = 1; i <= 20; i++) {
					ad.loopA(i);
				}
				
			}
		}, "A").start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				
				for (int i = 1; i <= 20; i++) {
					ad.loopB(i);
				}
				
			}
		}, "B").start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				
				for (int i = 1; i <= 20; i++) {
					ad.loopC(i);
					
					System.out.println("-----------------------------------");
				}
				
			}
		}, "C").start();
	}

}

class AlternateDemo{
	
	private int number = 1; //当前正在执行线程的标记
	
	private Lock lock = new ReentrantLock();
	private Condition condition1 = lock.newCondition();
	private Condition condition2 = lock.newCondition();
	private Condition condition3 = lock.newCondition();
	
	/**
	 * @param totalLoop : 循环第几轮
	 */
	public void loopA(int totalLoop){
		lock.lock();
		
		try {
			//1. 判断
			while(number != 1){
				condition1.await();
			}
			
			//2. 打印
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}
			
			//3. 唤醒
			number = 2;
			condition2.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void loopB(int totalLoop){
		lock.lock();
		
		try {
			//1. 判断
			while(number != 2){
				condition2.await();
			}
			
			//2. 打印
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}
			
			//3. 唤醒
			number = 3;
			condition3.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void loopC(int totalLoop){
		lock.lock();
		
		try {
			//1. 判断
			while(number != 3){
				condition3.await();
			}
			
			//2. 打印
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}
			
			//3. 唤醒
			number = 1;
			condition1.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
}

例6:用wait()、notify()实现阻塞队列

public class MyQueue {

	private final LinkedList<Object> list = new LinkedList<Object>();
	
	private final AtomicInteger count = new AtomicInteger(0);
	
	private final int maxSize;
	private final int minSize = 0;
	
	private final Object lock = new Object();
	
	public MyQueue (int maxSize){
		this.maxSize = maxSize;
	}

	public void put (Object obj) {
		synchronized(lock){
			while(count.get() == maxSize){
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			list.add(obj);
			count.getAndIncrement();
			System.out.println(" 元素 " + obj + " 被添加 ");
			lock.notify();
			
		}
	}
	
	public Object take(){
		Object temp = null;
		synchronized (lock) {
			while(count.get() == minSize){
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			count.getAndDecrement();
			temp = list.removeFirst();
			System.out.println(" 元素 " + temp + " 被消费 ");
			lock.notify();
		}
		return temp;
	}
	
	public int size(){
		return count.get();
	}
	
	
	public static void main(String[] args) throws Exception {
		
		final MyQueue m = new MyQueue(5);
		m.put("a");
		m.put("b");
		m.put("c");
		m.put("d");
		m.put("e");
		System.out.println("当前元素个数:" + m.size());
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				m.put("h");
				m.put("i");
			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
					Object t1 = m.take();
					//System.out.println("被取走的元素为:" + t1);
					Thread.sleep(1000);
					Object t2 = m.take();
					//System.out.println("被取走的元素为:" + t2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "t2");

		t1.start();
		Thread.sleep(1000);
		t2.start();
		
	}
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值