wait()注意以下几点:
1)wait()是属于Object类的方法。
2)调用了wait()之后会引起当前线程处于等待状态。
3)将当前线程置入“预执行队列”中,并且在wait()所在的代码行处停止执行,直到接到通知或被中断为止。
4)在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。
5)执行wait()后,当前线程释放锁;从wait()返回前,线程与其他线程竞争重新获得锁。
6)如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException异常,它是RuntimeException的一个子类,所以,不需要try-catch语句进行捕捉。
7)当线程呈wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常。
8)wait(1000)表示等待1000ms内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。
notify()注意以下几点:
1)notify()方法可以让该线程重新处于活动(就绪)状态,从而去抢夺锁。
2)如果多个线程同时处于等待状态,那么调用notify()方法只能随机唤醒一个线程。
3)在同一时间内,只有一个线程能够获得锁,执行完毕之后,则再将其释放供其它线程抢占。
//当list中的元素达到5个时发出通知notify
public class MyList {
private static List list = new ArrayList();
public static void add() {
list.add("aaa");
}
public static int size() {
return list.size();
}
}
//wait等待线程
public class WaitThread extends Thread{
private Object lock;
public WaitThread(Object lock) {
super();
this.lock = lock;
}
public void run() {
try {
synchronized(lock) {
if(MyList.size() != 5) {
System.out.println("wait begin:" + System.currentTimeMillis());
lock.wait();
System.out.println("wait end:" + System.currentTimeMillis());
}
}
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
//notify通知线程
public class NotifyThread extends Thread{
private Object lock;
public NotifyThread(Object lock) {
super();
this.lock = lock;
}
public void run() {
try {
synchronized(lock) {
for(int i = 0; i < 10; i++) {
MyList.add();
if(MyList.size() == 5) {
lock.notify();
System.out.println("已发出通知");
}
System.out.println("添加了" + (i + 1) + "个元素!");
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//测试线程
public class Main { public static void main(String[] args) {
try {
Object lock = new Object();
WaitThread wait = new WaitThread(lock);
wait.start();
Thread.sleep(50);
NotifyThread notify = new NotifyThread(lock);
notify.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
运行结果如下:
notify之后线程继续运行,并没有停止,而是把当前任务执行完后,才把锁由notify线程交给wait线程。
sleep()方法属于Thread类,从源码给出的解释来看,sleep()方法可以做到如下几点:
(1)首先,调用sleep()之后,会引起当前执行的线程进入暂时中断状态,也即睡眠状态。
(2)其次,虽然当前线程进入了睡眠状态,但是依然持有对象锁。
(3)在中断完成之后,自动进入唤醒状态从而继续执行代码【wait(long)也可以实现这个功能】。
得出如下结论:
(1)在线程的运行过程中,调用该线程持有对象锁的wait()方法时,该线程首先会进入等待状态,并将自己持有的对象锁释放。
(2)如果一个线程正处于等待状态时,那么唤醒它的办法就是开启一个新的线程,通过notify()或者notifyAll()的方式去唤醒。当然,需要注意的一点就是,必须是同一个对象锁。
(3)sleep()方法虽然会使线程中断,但是不会将自己的monitor对象释放,在中断结束后,依然能够保持代码继续执行。
join()的例子:
public class MyThread extends Thread{
public void run() {
try {
int secondValue = (int) (Math.random() * 10000);
System.out.println(secondValue);
Thread.sleep(secondValue);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public class Main { public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
thread.join();
System.out.println("mythread对象执行完毕再执行");
} catch(InterruptedException e) {
e.printStackTrace();
}
} }
运行结果:
main线程等待了4476ms才开始执行,也就是等待对象线程执行完毕后才执行。
join()的作用:使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码。能使线程由并行执行转为串行执行,具有同步的效果。
join与synchronized区别:join在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理做为同步。
join(long)使用wait(long)来实现,所以是否释放锁,与wait(long)类似。
查看join的源码如下:
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0; if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
} if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}