线程间的协作——notify、notifyAll,wait,Join

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;

猜你喜欢

转载自blog.csdn.net/qq_41700030/article/details/100071977