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)在同一时间内,只有一个线程能够获得锁,执行完毕之后,则再将其释放供其它线程抢占。
1 //当list中的元素达到5个时发出通知notify 2 public class MyList { 3 private static List list = new ArrayList(); 4 public static void add() { 5 list.add("aaa"); 6 } 7 public static int size() { 8 return list.size(); 9 } 10 } 11 //wait等待线程 12 public class WaitThread extends Thread{ 13 private Object lock; 14 public WaitThread(Object lock) { 15 super(); 16 this.lock = lock; 17 } 18 public void run() { 19 try { 20 synchronized(lock) { 21 if(MyList.size() != 5) { 22 System.out.println("wait begin:" + System.currentTimeMillis()); 23 lock.wait(); 24 System.out.println("wait end:" + System.currentTimeMillis()); 25 } 26 } 27 } catch(InterruptedException e) { 28 e.printStackTrace(); 29 } 30 } 31 } 32 //notify通知线程 33 public class NotifyThread extends Thread{ 34 private Object lock; 35 public NotifyThread(Object lock) { 36 super(); 37 this.lock = lock; 38 } 39 public void run() { 40 try { 41 synchronized(lock) { 42 for(int i = 0; i < 10; i++) { 43 MyList.add(); 44 if(MyList.size() == 5) { 45 lock.notify(); 46 System.out.println("已发出通知"); 47 } 48 System.out.println("添加了" + (i + 1) + "个元素!"); 49 Thread.sleep(1000); 50 } 51 } 52 } catch (InterruptedException e) { 53 e.printStackTrace(); 54 } 55 } 56 } 57 //测试线程 58 public class Main { 59 60 public static void main(String[] args) { 61 try { 62 Object lock = new Object(); 63 WaitThread wait = new WaitThread(lock); 64 wait.start(); 65 Thread.sleep(50); 66 NotifyThread notify = new NotifyThread(lock); 67 notify.start(); 68 } catch (InterruptedException e) { 69 e.printStackTrace(); 70 } 71 } 72 73 }
运行结果如下:
notify之后线程继续运行,并没有停止,而是把当前任务执行完后,才把锁由notify线程交给wait线程。
sleep()方法属于Thread类,从源码给出的解释来看,sleep()方法可以做到如下几点:
(1)首先,调用sleep()之后,会引起当前执行的线程进入暂时中断状态,也即睡眠状态。
(2)其次,虽然当前线程进入了睡眠状态,但是依然持有对象锁。
(3)在中断完成之后,自动进入唤醒状态从而继续执行代码【wait(long)也可以实现这个功能】。
得出如下结论:
(1)在线程的运行过程中,调用该线程持有对象锁的wait()方法时,该线程首先会进入等待状态,并将自己持有的对象锁释放。
(2)如果一个线程正处于等待状态时,那么唤醒它的办法就是开启一个新的线程,通过notify()或者notifyAll()的方式去唤醒。当然,需要注意的一点就是,必须是同一个对象锁。
(3)sleep()方法虽然会使线程中断,但是不会将自己的monitor对象释放,在中断结束后,依然能够保持代码继续执行。
join()的例子:
1 public class MyThread extends Thread{ 2 public void run() { 3 try { 4 int secondValue = (int) (Math.random() * 10000); 5 System.out.println(secondValue); 6 Thread.sleep(secondValue); 7 } catch(InterruptedException e) { 8 e.printStackTrace(); 9 } 10 } 11 } 12 public class Main { 13 14 public static void main(String[] args) { 15 try { 16 MyThread thread = new MyThread(); 17 thread.start(); 18 thread.join(); 19 System.out.println("mythread对象执行完毕再执行"); 20 } catch(InterruptedException e) { 21 e.printStackTrace(); 22 } 23 } 24 25 }
运行结果:
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; } } }