您的位置:首页 > 编程语言 > Java开发

java之join/wait/notify/notifyall

2014-05-17 18:08 218 查看

JOIN

一、使用方式。

join是Thread类的一个方法,启动线程后直接调用,例如:

1
Thread t =
new

AThread(); t.start(); t.join();

二、为什么要用join()方法

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。

三、join方法的作用

在JDk的API里对于join()方法是:

join

public final void join() throws InterruptedException Waits for this thread to die. Throws: InterruptedException  - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception
is thrown.

即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。

四、用实例来理解

写一个简单的例子来看一下join()的用法:

1.AThread 类

BThread类

TestDemo 类

01
class
BThread
extends
Thread {
02
    
public

BThread() {
03
        
super
(
"[BThread] Thread"
);
04
    
};
05
    
public

void
run() {
06
        
String threadName = Thread.currentThread().getName();
07
        
System.out.println(threadName +
" start."
);
08
        
try

{
09
            
for

(
int
i =
0
; i <
5
; i++) {
10
                
System.out.println(threadName +
" loop at "
+ i);
11
                
Thread.sleep(
1000
);
12
            
}
13
            
System.out.println(threadName +
" end."
);
14
        
}

catch
(Exception e) {
15
            
System.out.println(
"Exception from "

+ threadName +
".run"
);
16
        
}
17
    
}
18
}
19
class
AThread
extends
Thread {
20
    
BThread bt;
21
    
public

AThread(BThread bt) {
22
        
super
(
"[AThread] Thread"
);
23
        
this
.bt = bt;
24
    
}
25
    
public

void
run() {
26
        
String threadName = Thread.currentThread().getName();
27
        
System.out.println(threadName +
" start."
);
28
        
try

{
29
            
bt.join();
30
            
System.out.println(threadName +
" end."
);
31
        
}

catch
(Exception e) {
32
            
System.out.println(
"Exception from "

+ threadName +
".run"
);
33
        
}
34
    
}
35
}
36
public
class

TestDemo {
37
    
public

static
void
main(String[] args) {
38
        
String threadName = Thread.currentThread().getName();
39
        
System.out.println(threadName +
" start."
);
40
        
BThread bt =
new
BThread();
41
        
AThread at =
new
AThread(bt);
42
        
try

{
43
            
bt.start();
44
            
Thread.sleep(
2000
);
45
            
at.start();
46
            
at.join();
47
        
}

catch
(Exception e) {
48
            
System.out.println(
"Exception from main"
);
49
        
}
50
        
System.out.println(threadName +
" end!"
);
51
    
}
52
}

打印结果:

01
main start.    //主线程起动,因为调用了at.
join
(),要等到at结束了,此线程才能向下执行。
02
[BThread] Thread start.
03
[BThread] Thread loop at 0
04
[BThread] Thread loop at 1
05
[AThread] Thread start.    //线程at启动,因为调用bt.
join
(),等到bt结束了才向下执行。
06
[BThread] Thread loop at 2
07
[BThread] Thread loop at 3
08
[BThread] Thread loop at 4
09
[BThread] Thread end.
10
[AThread] Thread end.    // 线程AThread在bt.
join
();阻塞处起动,向下继续执行的结果
11
main end!      //线程AThread结束,此线程在at.
join
();阻塞处起动,向下继续执行的结果。
修改一下代码:

01
public
class

TestDemo {
02
    
public

static
void
main(String[] args) {
03
        
String threadName = Thread.currentThread().getName();
04
        
System.out.println(threadName +
" start."
);
05
        
BThread bt =
new
BThread();
06
        
AThread at =
new
AThread(bt);
07
        
try

{
08
            
bt.start();
09
            
Thread.sleep(
2000
);
10
            
at.start();
11
            
//at.join(); //在此处注释掉对join()的调用
12
        
}

catch
(Exception e) {
13
            
System.out.println(
"Exception from main"
);
14
        
}
15
        
System.out.println(threadName +
" end!"
);
16
    
}
17
}

打印结果:

01
main start.    // 主线程起动,因为Thread.
sleep
(2000),主线程没有马上结束;
02
 
03
[BThread] Thread start.    //线程BThread起动
04
[BThread] Thread loop at 0
05
[BThread] Thread loop at 1
06
main end!   // 在
sleep
两秒后主线程结束,AThread执行的bt.
join
();并不会影响到主线程。
07
[AThread] Thread start.    //线程at起动,因为调用了bt.
join
(),等到bt结束了,此线程才向下执行。
08
[BThread] Thread loop at 2
09
[BThread] Thread loop at 3
10
[BThread] Thread loop at 4
11
[BThread] Thread end.    //线程BThread结束了
12
[AThread] Thread end.    // 线程AThread在bt.
join
();阻塞处起动,向下继续执行的结果

五、从源码看join()方法

在AThread的run方法里,执行了bt.join();,进入看一下它的JDK源码:

1
public
final

void
join()
throws

InterruptedException {
2
    
join(0L);
3
}
然后进入join(0L)方法:

01
public
final

synchronized
void
join(
long
l)
02
    
throws

InterruptedException
03
{
04
    
long

l1 = System.currentTimeMillis();
05
    
long

l2 = 0L;
06
    
if
(l < 0L)
07
        
throw

new
IllegalArgumentException(
"timeout value is negative"
);
08
    
if
(l == 0L)
09
        
for
(; isAlive(); wait(0L));
10
    
else
11
        
do
12
        
{
13
            
if
(!isAlive())
14
                
break
;
15
            
long

l3 = l - l2;
16
            
if
(l3 <= 0L)
17
                
break
;
18
            
wait(l3);
19
            
l2 = System.currentTimeMillis() - l1;
20
        
}

while
(
true
);
21
}
单纯从代码上看: * 如果线程被生成了,但还未被起动,isAlive()将返回false,调用它的join()方法是没有作用的。将直接继续向下执行。 * 在AThread类中的run方法中,bt.join()是判断bt的active状态,如果bt的isActive()方法返回false,在bt.join(),这一点就不用阻塞了,可以继续向下进行了。从源码里看,wait方法中有参数,也就是不用唤醒谁,只是不再执行wait,向下继续执行而已。 * 在join()方法中,对于isAlive()和wait()方法的作用对象是个比较让人困惑的问题:

isAlive()方法的签名是:public final native boolean isAlive(),也就是说isAlive()是判断当前线程的状态,也就是bt的状态。

wait()方法在jdk文档中的解释如下:

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method.
The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

在这里,当前线程指的是at。此处就是为什么阻塞的是at,让bt继续执行下去,直到bt执行完成后,再执行at。转载http://www.open-open.com/lib/view/open1371741636171.html

WAIT/NOTIFY/NITIFYALL

在多线程的情况下,由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

   wait与notify是java同步机制中重要的组成部分。结合与synchronized关键字使用,可以建立很多优秀的同步模型。

  synchronized(this){ }等价于publicsynchronized void method(){.....}

   同步分为类级别和对象级别,分别对应着类锁和对象锁。类锁是每个类只有一个,如果static的方法被synchronized关键字修饰,则在这个方法被执行前必须获得类锁;对象锁类同。

   首先,调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在作用等同于synchronized(obj){......}的内部才能够去调用obj的wait与notify/notifyAll三个方法,否则就会报错:

  java.lang.IllegalMonitorStateException:current thread not owner

  在调用wait的时候,线程自动释放其占有的对象锁,同时不会去申请对象锁。当线程被唤醒的时候,它才再次获得了去获得对象锁的权利。

  所以,notify与notifyAll没有太多的区别,只是notify仅唤醒一个线程并允许它去获得锁,notifyAll是唤醒所有等待这个对象的线程并允许它们去获得对象锁,只要是在synchronied块中的代码,没有对象锁是寸步难行的。其实唤醒一个线程就是重新允许这个线程去获得对象锁并向下运行。

   notifyAll,虽然是对每个wait的对象都调用一次notify,但是这个还是有顺序的,每个对象都保存这一个等待对象链,调用的顺序就是这个链的顺序。其实启动等待对象链中各个线程的也是一个线程,在具体应用的时候,需要注意一下。

  wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能。因为都个对像都有锁,锁是每个对像的基础,当然操作锁的方法也是最基础了。

wait():

等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateException。

调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。

notify():

唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

notifyAll():

唤醒所有等待的线程,注意唤醒的是notify之前wait的线程,对于notify之后的wait线程是没有效果的。

 

通常,多线程之间需要协调工作:如果条件不满足,则等待;当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。

例如:

  synchronized(obj) {

  while(!condition) {

  obj.wait();

  }

  obj.doSomething();

  }

  

  当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。

  在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A :

  

  synchronized(obj) {

  condition = true;

  obj.notify();

  }

  
  需要注意的概念是:
  

  # 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj){...}代码段内。

  

  # 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj){...}代码段内唤醒A。

  

  # 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。

  

  #如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。

  

  #obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。

  

  # 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
  

谈一下synchronized和wait()、notify()等的关系:

1.有synchronized的地方不一定有wait,notify

2.有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。

另外,注意一点:如果要把notify和wait方法放在一起用的话,必须先调用notify后调用wait,因为如果调用完wait,该线程就已经不是currentthread了。
转载:http://blog.csdn.net/oracle_microsoft/article/details/6863662
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java