notify和notifyAll 的作用是唤醒正在wait的线程,三者都是Object的方法,notify是随机唤醒wait线程中的一个,notifyAll 则是唤醒全部。
1).执行notify、notifyAll 方法的前提是当前线程已经获取到对象的锁,也就是必须在synchronized修饰的代码块或者方法中使用。这个和wait是一样的。
2).被调用notify()或者notifyAll()后,线程还是会等待,直到拥有锁的所有权,才会继续往下执行。
3)尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况
举个栗子
public synchronized void waitSite(){//地点和CITY相同时进入等待状态
while(CITY.equals(this.site)) {
try {
wait();
System.out.println("check site thread["+Thread.currentThread().getId()
+"] is be notifed.");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("the site is"+this.site+",I will call user.");
}
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<3;i++){//起三个检查地点变化的线程,并通过条件判断使其进入wait()方法
new CheckSite().start();
}
for(int i=0;i<3;i++){//三个检查距离变化的线程,并通过条件判断使其进入wait()方法
new CheckKm().start();
}
Thread.sleep(1000);
//快递地点变化,通过调用notifyAll,唤醒检查地点(和原来的是否相同)和距离的线程,进行业务处理
express.changeKm();
}
上面是一个快递的栗子,有两个属性,距离,收货地点,当其中一个发生变化时,另一个属性需要立即进行判断修改,这里采用多线程的方式实现。当调用notify时我们只能唤醒一个线程,并且不确定是哪一个。
最后再讲两个重要的方法
yield方法 让出当前线程的cpu时间片,由运行中状态转换成就绪状态(和sleep方法区别在于sleep制定的时间段内,cpu不会再去调度该线程,但yield方法退出cpu后可能下一刻立即获取抢占到时间片)
Join方法:调用一个新的线程加入到本线程前面执行,该线程执行完,自己再执行
public static class G1 extends Thread{//继承Thread类创建线程
private Thread thread;
public G1(Thread thread){
this.thread=thread;
}
@Override
public void run() {
System.out.println("G1开始运行:"+Thread.currentThread().getName());
try {
if(thread != null){
thread.join();//让此线程优先
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("G1运行结束:"+Thread.currentThread().getName());
}
}
public static class G2 extends Thread{
private Thread g3;
public G2(Thread g3) {
this.g3=g3;
}//继承Thread类创建线程
@Override
public void run() {
System.out.println("G2开始运行:"+Thread.currentThread().getName());
try {
if(g3 != null){
g3.join();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("G2结束运行:"+Thread.currentThread().getName());
}
}
public static class G3 implements Runnable{//实现Runnable接口创建线程
@Override
public void run() {
System.out.println("G3开始运行:"+Thread.currentThread().getName());
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("G3结束运行:"+Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
Thread g3 = new Thread(new G3());
G2 g2 = new G2(g3);
G1 g1 = new G1(g2);
g1.start();
g2.start();
g3.start();
}
其实还有另一种简单的写法,能够保证线程顺序执行,如在main方法 中new了三个线程t1,t2,t3
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();
最后再巩固一下notifyAll,wait的使用机制,假如要实现一个连接池的话,可以用以下写法,来防止wait的方法被提前唤醒,但连接被别的线程抢走而不再等待直接返回null
long overtime = now+T;
long remain = T;//等待的持续时间
while(result不满足条件&& remain>0){
wait(remain);
remain = overtime – now;//wait被动唤醒后,未等待的剩余时间
}
return result;
补充,join方法的实现原理也是不断的轮询wait;