wait(),sleep(),notify(),join()

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)在同一时间内,只有一个线程能够获得锁,执行完毕之后,则再将其释放供其它线程抢占。

notifyAll()就是用来唤醒正在等待状态中的所有线程的:
(1)notifyAll()只会唤醒那些等待抢占指定对象锁的线程,其他线程则不会被唤醒。
(2)notifyAll()只会一个一个的唤醒,而并非统一唤醒。因为在同一时间内,只有一个线程能够持有对象锁。
(3)notifyAll()只是随机的唤醒线程,并非有序唤醒。
 
注意:执行notify()后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获得该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续组赛在wait状态,直到这个对象发出一个notify或notifyAll。
例子如下:
 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 }
View Code

运行结果如下:

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 }
View Code

运行结果:

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;
            }
        }
    }

 

posted on 2018-03-22 11:49  二十年后20  阅读(321)  评论(0编辑  收藏  举报

导航