wait/notify 方法线程间的通信,只随机通知一个线程进行唤醒,一次性唤醒所有线程, wait(long) 的使用,通知过早会打乱顺序正常的逻辑顺序

一.wait()、notify()和notifyAll()

  wait()、notify()和notifyAll()是Object类中的方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

/**

 * Wakes up a single thread that is waiting on this object's

 * monitor. If any threads are waiting on this object, one of them

 * is chosen to be awakened. The choice is arbitrary and occurs at

 * the discretion of the implementation. A thread waits on an object's

 * monitor by calling one of the wait methods

 */

public final native void notify();

 

/**

 * Wakes up all threads that are waiting on this object's monitor. A

 * thread waits on an object's monitor by calling one of the

 * wait methods.

 */

public final native void notifyAll();

 

/**

 * Causes the current thread to wait until either another thread invokes the

 * {@link java.lang.Object#notify()} method or the

 * {@link java.lang.Object#notifyAll()} method for this object, or a

 * specified amount of time has elapsed.

 * <p>

 * The current thread must own this object's monitor.

 */

public final native void wait(long timeout) throws InterruptedException;

   从这三个方法的文字描述可以知道以下几点信息:

  1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。

  2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)

  3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;

  4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

  有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。

  上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

  调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);

  notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。

  同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

  nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。

  这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。

package com.bjsxt.base.conn008;

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

public class ListAdd1 {

	private volatile static List list = new ArrayList();	
	
	public void add(){
		list.add("bjsxt");
	}
	public int size(){
		return list.size();
	}
	
	public static void main(String[] args) {
		
		final ListAdd1 list1 = new ListAdd1();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for(int i = 0; i <10; i++){
						list1.add();
						System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
						Thread.sleep(500);
					}	
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				while(true){
					if(list1.size() == 5){
						System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " list size = 5 线程停止..");
						throw new RuntimeException();
					}
				}
			}
		}, "t2");		
		//t1 t2 谁在前无所谓,因为是异步的
		t1.start();
		t2.start();
	}
	
	
}

结果为 只要是 t2线程 list size = 5 线程就停止了抛出异常

当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程收到通知:t2 list size = 5 线程停止..
Exception in thread "t2" java.lang.RuntimeException
	at com.bjsxt.base.conn008.ListAdd1$2.run(ListAdd1.java:42)
	at java.lang.Thread.run(Thread.java:722)
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..

 

方法wait() 的作用是使当前执行代码的线程进行等待,wait() 方法是object 类的方法,该方法用来将当前线程置入“预执行队列中”,并且在wait() 所在的代码行处停止执行,直到接到通知或被中断为止,在调用wait() 之前,线程必须获得该对象的对象级别锁,即只能是在同步方法或同步块中调用wait() 方法,在执行wait() 方法后,当前线程释放锁,在wait() 返回前,线程与其他线程竞争重新获得所,如果调用wait() 时没有持有适当的锁,则跑出异常,IIIegalMonitorStateException 它是RuntimeException 的一个子类,因此,不需要try-catch 语句进行捕捉异常

方法notify() 也要在同步方法或者同步块中调用,即在调用前,线程也必须获得该对象的级别锁,如果调用notify() 时没有持有适当的锁,也会抛出IIIegalMonitorStateException 该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选一个呈wait 状态的线程,对其发出通知notify ,并使它等待获取该对象的对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify() 方法的线程将程序执行完,也就是退出synchronized 代码块以后,当前线程才会释放锁,而呈wait 状态所在的线程才可以获取该对象锁,当第一个获得了该对象的wait 线程运行完毕以后,它会释放掉该对象的锁,此时如果没有再次使用notify 语句,则即便对该对象已经空闲,其他wait 状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait 状态,直到这个对象发出一个notify或者notifyAll .

package test;

public class Test1 {
	public static void main(String[] args) {
		try {
			String newString = new String("");
			newString.wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

 

Exception in thread "main" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:485)
	at test.Test1.main(Test1.java:7)

出现异常的原因是没有“对象监视器”,也就是没有加同步锁

package test;
public class Test2 {
	public static void main(String[] args) {
		try {
			String lock = new String();
			System.out.println("syn上面");
			synchronized (lock) {
				System.out.println("syn第一行");
				lock.wait();
				System.out.println("wait下的代码!");
			}
			System.out.println("syn下面的代码");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

​

结果为

syn上面
syn第一行

把上述的代码修改为wait/notify 的方式

package com.bjsxt.base.conn008;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
 * @author alienware
 *
 */
public class ListAdd2 {
	private volatile static List list = new ArrayList();	
	
	public void add(){
		list.add("bjsxt");
	}
	public int size(){
		return list.size();
	}
	
	public static void main(String[] args) {
		
		final ListAdd2 list2 = new ListAdd2();
		final Object lock = new Object();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
                                        //t1线程拿着锁,就不释放了,直到线程运行完毕以后才会运行t2 
					synchronized (lock) {
						System.out.println("t1启动..");
						for(int i = 0; i <10; i++){
							list2.add();
							System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
							Thread.sleep(500);
							if(list2.size() == 5){
								System.out.println("已经发出通知..");
								lock.notify();
							}
						}						
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (lock) {
					System.out.println("t2启动..");
					if(list2.size() != 5){
						try {
							lock.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
					throw new RuntimeException();
				}
			}
		}, "t2");
                //t2 线程一定是在前面的,程序运行到lock.wait(); 由于开始进来的时候,list 的一定是不等于5的,所以就一直等待,由于wait 等待的时候是释放锁的,所以锁释放了开始运行t1线程
		t2.start();
		t1.start();
		
	}
	
}

结果是

t2启动..
t1启动..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
已经发出通知..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t2收到通知线程停止..
Exception in thread "t2" java.lang.RuntimeException
	at com.bjsxt.base.conn008.ListAdd2$2.run(ListAdd2.java:60)
	at java.lang.Thread.run(Thread.java:722)

但是这样写有一个弊端,不能做到实时通知的效果应该改为下面这个样子

package com.bjsxt.base.conn008;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
 * @author alienware
 *
 */
public class ListAdd2 {
	private volatile static List list = new ArrayList();
	//是线程包当中的类加上了这个就可以做到实时通知的目的,但是这个类不能与synchronized 合在一起用
	final static CountDownLatch countDownLatch =new CountDownLatch(2);
	
	public void add(){
		list.add("bjsxt");
	}
	public int size(){
		return list.size();
	}
	
	public static void main(String[] args) {
		
		final ListAdd2 list2 = new ListAdd2();
		final Object lock = new Object();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					//CountDownLatch不能与synchronized 合在一起用
//					synchronized (lock) {
						System.out.println("t1启动..");
						for(int i = 0; i <10; i++){
							list2.add();
							System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
							Thread.sleep(500);
							if(list2.size() == 5){
								System.out.println("已经发出通知..");
//								lock.notify();
								countDownLatch.countDown();
								countDownLatch.countDown();
							}
//						}						
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				CountDownLatch不能与synchronized 合在一起用
//				synchronized (lock) {
					System.out.println("t2启动..");
					if(list2.size() != 5){
						try {
//							lock.wait();
							countDownLatch.await();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
					throw new RuntimeException();
//				}
			}
		}, "t2");	
		t2.start();
		t1.start();
		
	}
	
}

只随机通知一个线程进行唤醒

源码 notifyOne

package extthread;
import service.Service;
public class NotifyThread extends Thread {
	private Object lock;
	public NotifyThread(Object lock) {
		super();
		this.lock = lock;
	}
	@Override
	public void run() {
		synchronized (lock) {
			lock.notify();
			lock.notify();
			lock.notify();
			lock.notify();
			lock.notify();
			lock.notify();
			lock.notify();
			lock.notify();
			lock.notify();
		}
	}
}

一共是ThreadA ThreadB ThreadC 三个类的代码是完全一样的

package extthread;
import service.Service;
public class ThreadA extends Thread {
	private Object lock;
	public ThreadA(Object lock) {
		super();
		this.lock = lock;
	}
	@Override
	public void run() {
		Service service = new Service();
		service.testMethod(lock);
	}
}
package service;
public class Service {
	public void testMethod(Object lock) {
		try {
			synchronized (lock) {
				System.out.println("begin wait() ThreadName="
						+ Thread.currentThread().getName());
				lock.wait();
                     //每一个线程进来以后,锁都释放了,ThreadA ThreadB ThreadC  三种线程
				System.out.println("  end wait() ThreadName="
						+ Thread.currentThread().getName());
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
package test;
import extthread.NotifyThread;
import extthread.ThreadA;
import extthread.ThreadB;
import extthread.ThreadC;
public class Test {
	public static void main(String[] args) throws InterruptedException {
		Object lock = new Object();
		ThreadA a = new ThreadA(lock);
		a.start();
		ThreadB b = new ThreadB(lock);
		b.start();
		ThreadC c = new ThreadC(lock);
		c.start();
		Thread.sleep(1000);
		NotifyThread notifyThread = new NotifyThread(lock);
		notifyThread.start();
	}
}

随机会唤醒一个线程

begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-2
begin wait() ThreadName=Thread-0
  end wait() ThreadName=Thread-1
  end wait() ThreadName=Thread-0
  end wait() ThreadName=Thread-2

多次调用notify() 方法唤醒了全部waiting 中的线程

一次性唤醒所有线程

package extthread;

public class NotifyThread extends Thread {
	private Object lock;
	public NotifyThread(Object lock) {
		super();
		this.lock = lock;
	}
	@Override
	public void run() {
		synchronized (lock) {
			lock.notifyAll();
		}
	}
}

一次性唤醒所有线程

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

 wait(long) 的使用

是等待某一个时间是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒

源码 waitHasParamMethod

package myrunnable;
public class MyRunnable {
	static private Object lock = new Object();
	static private Runnable runnable1 = new Runnable() {
		@Override
		public void run() {
			try {
				synchronized (lock) {
					System.out.println("wait begin timer="
							+ System.currentTimeMillis());
					lock.wait(5000);
					System.out.println("wait   end timer="
							+ System.currentTimeMillis());
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	};
	static private Runnable runnable2 = new Runnable() {
		@Override
		public void run() {
			synchronized (lock) {
				System.out.println("notify begin timer="
						+ System.currentTimeMillis());
				lock.notify();
				System.out.println("notify   end timer="
						+ System.currentTimeMillis());
			}
		}
	};
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Thread(runnable1);
		t1.start();
		Thread.sleep(3000);
		Thread t2 = new Thread(runnable2);
		t2.start();
	}
}
wait begin timer=1533888100845
notify begin timer=1533888103846
notify   end timer=1533888103847
wait   end timer=1533888103847

通知过早会打乱顺序正常的逻辑顺序

源码 firstNotify

package test;
public class MyRun {
	private String lock = new String("");
	private boolean isFirstRunB = false;
	private Runnable runnableA = new Runnable() {
		@Override
		public void run() {
			try {
				synchronized (lock) {
					while (isFirstRunB == false) {
						System.out.println("begin wait");
						lock.wait();
						System.out.println("end wait");
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	};
	private Runnable runnableB = new Runnable() {
		@Override
		public void run() {
			synchronized (lock) {
				System.out.println("begin notify");
				lock.notify();
				System.out.println("end notify");
				isFirstRunB = true;
			}
		}
	};
	public static void main(String[] args) throws InterruptedException {
		MyRun run = new MyRun();
		Thread a = new Thread(run.runnableA);
		a.start();
		Thread.sleep(100);//把这个注释掉永远不会被通知
		Thread b = new Thread(run.runnableB);
		b.start();
	}
}

如果先通知了,则wait 方法也就没有必要执行了

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值