java线程的等待与通知

线程的等待与通知

wait()函数

使用的时候要注意,要调用共享变量对象的wait()方法,必须先获取该对象的Monitor。否则会抛出IllegalMonitorStateException;

当调用一个共享变量的wait()方法时,该调用线程会被阻塞挂起,直到:1)其他线程调用了该共享变量的notify()或notifyAll()方法;2)其他线程调用了该线程的interrupt()方法,该线程抛出InterruptedException异常返回。

import java.util.PriorityQueue;

import com.tim.base.easystart.util.VeDate;

/**
 * 1, wait使用之前需要获取到操作对象的Monitor
 * 2, wait之后会释放锁定的Monitor
 * 3, notifyAll会唤醒等待该对象的Monitor的所有线程,具体谁来获取到这个Monior是随机的
 */
public class WaitTest {
	
	static PriorityQueue<String> queue=new PriorityQueue<String>();  
	
	public static void add(String content) throws InterruptedException {
		synchronized (queue) {
			while (queue.size() > 10) {
				System.out.println("The Queue is Over 10...");
				queue.wait();
			}
			queue.add(content);
			queue.notifyAll();
		}
	}
	
	public static String getQueue() {
		synchronized (queue) {
			while (queue.size() == 0) {
				try {
					System.out.println("The Queue is Empty...");
					queue.wait();
				} 
				catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			String result = queue.poll();
			queue.notifyAll();
			return result;
		}
	}
	
	public static void main(String[] args) {
		
		Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					try {
						add(VeDate.getNo(5));
					} 
					catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(getQueue());
				}
			}
		});
		
		t1.start();
		
		t2.start();
		
	}
	
}

//附上工具方法
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

public final class VeDate {

    private VeDate() {

    }

    /**
     * 根据用户传入的时间表示格式,返回当前时间的字符串
     * @param sformat
     *                  yyyyMMddhhmmss
     * @return
     */
    public static String getUserDate(String sformat) {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat(sformat);
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    /**
     * 生成格式为yyyymmddhhmmss+k位随机数
     *
     * @param k
     *            表示是取几位随机数,可以自己定
     * @return
     */

    public static String getNo(int k) {
        return getUserDate("yyyyMMddHHmmss") + getRandom(k);
    }

    /**
     * 返回一个随机数
     *
     * @param i
     * @return
     */
    public static String getRandom(int i) {
        Random jjj = new Random();
        if (i == 0) {
            return "";
        }
        StringBuffer jj = new StringBuffer();
        for (int k = 0; k < i; k++) {
            jj.append(jjj.nextInt(9));
        }
        return jj.toString();
    }

}

wait()方法的常见使用方式:

synchronized (obj) {
	whie(条件不满足) {
        obj.wait();
    }
}

不使用while来判断条件,可能会出现虚假唤醒,关于虚假唤醒可以点击查看wait()-虚假唤醒

当前线程调用共享变量的wait()方法之后,只会释放当前共享变量上的锁,如果当前线程还持有其他共享变量的锁,则这些锁是不会被释放的。

wait()函数调用后被打断

public class WaitInterrupt {

	public static Object obj = new Object();
	
	public static void main(String[] args) throws InterruptedException {
		
		Thread thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("thread run begin");
				synchronized (obj) {
					try {
						obj.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("thread run end");
			}
		});
		
		thread.start();
		
		Thread.sleep(1000);
		
		System.out.println("Interrupt start");
		thread.interrupt();
		System.out.println("Interrupt end");
	}
	
}

wait(long timeout)和wait(long timeout, int nanos)

wait(long timeout)比wait()函数多了一个超时参数,如果一个线程调用共享对象的wait(long timeout)方法后,没有在timeout ms时间内,被其它线程调用该变量的notify()或notifyAll()方法唤醒,那么函数还是会因为超时而返回。

timeout设置为0效果等同于wait();

    public final void wait() throws InterruptedException {
        wait(0);
    }

timeout设置小于0会抛出IllegalArgumentException异常

   /**
     * @param      timeout   the maximum time to wait in milliseconds.
     * @throws  IllegalArgumentException      if the value of timeout is
     *               negative.
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @throws  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    public final native void wait(long timeout) throws InterruptedException;

wait(long timeout, int nanos)

    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

notify()

一个线程调用共享对象的notify()方法之后,会唤醒一个在该共享变量上调用wait系列方法后被挂起的线程。如果有多个线程都调用wait方法,那么具体唤醒哪个线程是随机的。

被唤醒后不能立刻从wait方法返回并继续执行,它必须获取共享对象的监视器锁之后才可以返回。所以说唤醒它的线程释放了共享变量的监视器锁之后,被唤醒的线程也不一定会获取到共享对象的监视器锁,该线程必须和其它线程一起竞争这个锁,只有竞争到共享变量的监视器锁之后才可以继续执行。

调用该方法之前,该线程必须获取到共享对象的监视器锁,这个要求和wait()方法是一致的,否则会抛出InterruptedException异常。

notifyAll()

一个线程调用共享对象的notifyAll()方法之后,会唤醒所有由于调用该共享对象wait系列方法被阻塞的所有线程。

public class NotifyTest {
	private static volatile Object resourceA = new Object();
	
	public static void main(String[] args) throws InterruptedException {
		Thread threadA = new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (resourceA) {
					System.out.println("threadA get resourceA lock");
					try {
						System.out.println("threadA begin wait");
						resourceA.wait();
						System.out.println("threadA end wait");
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		
		Thread threadB = new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (resourceA) {
					System.out.println("threadB get resourceA lock");
					try {
						System.out.println("threadB begin wait");
						resourceA.wait();
						System.out.println("threadB end wait");
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		
		Thread threadC = new Thread(new Runnable() {
			
			@Override
			public void run() {
				synchronized (resourceA) {
					System.out.println("threadC begin notify");
					resourceA.notify();
				}
			}
		});
		
		//启动线程
		threadA.start();
		threadB.start();
		Thread.sleep(1000); //等待线程A和B都执行到wait方法
		threadC.start();
		
		//等待线程结束
		threadA.join();
		threadB.join();
		threadC.join();
		
		System.out.println("main over");
	}
}

输出:

threadB get resourceA lock
threadB begin wait
threadA get resourceA lock
threadA begin wait
threadC begin notify
threadB end wait

最终只有一个线程被唤醒,如果将resourceA.notify();修改为resourceA.notifyAll();

输出:

threadB get resourceA lock
threadB begin wait
threadA get resourceA lock
threadA begin wait
threadC begin notify
threadA end wait
threadB end wait
main over

从结果还可以理解下,线程A和B都被唤醒了,但是线程A先竞争到了resourceA的监视器锁,所以先执行完毕了,释放了监视器锁之后,线程B也拿到了这个Monitor,然后紧接着执行完毕,最终主线程执行完毕。

关于join方法我们后续再讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值