多线程编程 -wait(),notify()/notityAll()方法

多线程编程 -wait(),notify()/notityAll()方法

[wait(),notify()/notityAll()方法]
Q’V.]h  


m8{B/_ Xh  

  关于这两个方法,有很多的内容需要说明.在下面的说明中可能会有很多地方不能一下子
8asRZ*o  

明白,但在看完本节后,即使不能完全明白,你也一定要回过头来记住下面的两句话:
pRCd1ONk  


1 /p;z-  

[wait(),notify()/notityAll()方法是普通对象的方法(Object超类中实现),而不是线程对象的方法]
xaqzw *  


_VRG(q?_  

[wait(),notify()/notityAll()方法只能在同步方法中调用]
_@Rkh/7  


sWZA3^  


}bu5O;CZ.v  

  [线程的互斥控制]
HX!a)pe  

 
[JOXIG_ y  

  多个线程同时操作某一对象时,一个线程对该对象的操作可能会改变其状态,而该状态会影响
%D&wl_3W  

另一线程对该对象的真正结果.
[mBqa~txr  

  这个例子我们在太多的文档中可以看到,就象两个操售票员同时售出同一张票一样.
Nh7y’=L*>2  


*e`TBczB^  

  线程A                           线程B
sviY?5h>  


HP;T+Sq  

1.线程A在数据库中查询存票,发现票C可以卖出
ya)CR#  

2.线程A接受用户订票请求,准备出票.
}eZx,4tC  

3.                                 这时切换到了线程B执行
-GUi+gZna  

4.                                 线程B在数据库中查询存票,发现票C可以卖出                
)O;aVHP_T  

5.                                 线程B将票卖了出去
ue }l -~W  

6.切换到线程A执行,线程A卖了一张已经卖出的票
.Fs.ny%&r  


? w^fsT^P  

所以需要一种机制来管理这类问题的发生,当某个线程正在执行一个不可分割的部分时,其它线程不能
enBT+f_E  

不能同时执行这一部分.
7D ddSX>/  


O3 � [z(  

象这种控制某一时刻只能有一个线程执行某个执行单元的机制就叫互斥控制或共享互斥(mutual exclusion)
T<CpTD  


_’1>X)D  

  在JAVA中,用synchornized关键字来实现互斥控制(暂时这样认为,JDK1.5已经发展了新的机制)
“QX’�;f6)  


2$ : 9″  

  [synchornized关键字]
d9nHt]m.  

  把一个单元声明为synchornized,就可以让在同一时间只有一个线程操作该方法.
m:G!Vm3  

 
L t@Y  

  有人说synchornized就是一把锁,事实上它确实存在锁,但是是谁的锁,锁谁,这是一个非常复杂的
4C6>(w7}’  

问题.
.-_Ks8Gq”  


L6U!_ts^T  

  每个对象只有一把监视锁(monitor lock),一次只能被一个线程获取.当一个线程获取了这一个锁
hF=/h. p  

后,其它线程就只能等待这个线程释放锁才能再获取.
zw7’s|  


ziH$M  

  那么synchornized关键字到底锁什么?得到了谁的锁?
[:#fcac’]  

 
, !>$  

  对于同步块,synchornized获取的是参数中的对象锁:
:uv(U7F  


Ri8m//X<  

  synchornized(obj){
`)B?c0″  

    //……………
r5`Y,b|z  

  }
+p:�/VU S  

  线程执行到这里时,首先要获取obj这个实例的锁,如果没有获取到线程只能等待.如果多个线程
<aO7gC6t  

执行到这里,只能有一个线程获取obj的锁,然后执行{}中的语句,所以,obj对象的作用范围不同,控制程序
0&#V?q$/R>  

不同.
Ckt9y% |  

 
/? s;h%d  

  假如:
|.�7Z{<t|  

  public void test(){
szth]>hw;  

    Object o = new Object();
>*c{M0{o  

   
^/9ayB=uB  

    synchornized(obj){
W12Q[N?xy  

        //……………
S5$?mg0^  

    }
@y”q0s  

  }
‘P(CX c  


<wYT@*6j  

  这段程序控制不了任何,多个线程之间执行到Object o = new Object();时会各自产生一个对象
+.c5 `4Z  

然后获取这个对象有监视锁,各自皆大欢喜地执行.
Kn(O’UN  


E3v=tyg  

  而如果是类的属性:
*FR0-HlR  


Fsj l &  

  class Test{
F%gtFY)!=e  

    Object o = new Object();
u9�@IPie  

    public void test(){
kp!lBo~W  


AF8++H  

        synchornized(o){
?H.>nS_  

          //……………
BQGR )N1f  

        }
[z&?LX/:D  

    }
^qH|x>Z  

  }
y a]  


#se},e HN  

  所有执行到Test实例的synchornized(o)的线程,只有一个线程可以获取到监视锁.
b*,oETCH  


plI )Q  

  有时我们会这样:
x]nb_wv*  

    public void test(){
|wyi%?bmB  


zw1^&~8x.  

        synchornized(this){
d>^cb -@K  

          //……………
k+’GeBX-K  

        }
U`pub>;EZ;  

    }
EW^ a<  

  那么所有执行Test实例的线程只能有一个线程执行.而synchornized(o)和
#w�te?!W  

synchornized(this)的范围是不同的,因为执行到Test实例的synchornized(o)的线程等待时,其它线程可以执行
mw}�n+  

Test实例的synchornized(o1)部分,但多个线程同时只有一个可以执行Test实例的synchornized(this).
FRu7F/3wg-  

 
PHUy5S=  

  而对于   synchornized(Test.class){
6H1/vwc<  

          //……………
>RosCW6  

    }这样的同步块而言,所有调用Test多个实例的线程赐教只能有一个线程可以执行.
*+oJ(e  


S ,rObj  


=Dn[En)W  

 
:ZnmoZe  


$du0IH4[$}  

  [synchornized方法]
7|1~cgT  


CA2Yc03[  

  如果一个方法声明为synchornized的,则等同于把在为个方法上调用synchornized(this).
@S;oh  


@W|]bc@E {  

  如果一个静态方法被声明为synchornized,则等同于把在为个方法上调用synchornized(类.class).
p>0sD+tL  


8D;?~ig  

 
_?yfLal-  


9″9@q7sU%  


‘J5HgLF  


}*�9R=7  


#EFhE=`:  

现在进入wait方法和notify/notifyAll方法.这两个(或叫三个)方法都是Object对象的方法,而不是
Tbc <T  

线程对象的方法.如同锁一样,它们是在线程中调用某一对象上执行的.
cQMHJ7Z*6  


rqxCjh/D  

  class Test{
`*J’|P^J,  

    public synchornized void test(){
B<WW’`ubj  

    //获取条件,int x 要求大于100;
9I,~  

   
gd;sr  

        if(x < 100)
?Mu$ 8iy  

          wait();
oz!{Fd-  

    }
)_v {M  

  }
7LUkb  


3Z+*jzvh3*  

  这里为了说明方法没有加在try{}catch(){}中,如果没有明确在哪个对象上调用wait()方法,则
D>BnkB  

为this.wait();
v”,3Rp/ v$  

  假如:
%9T.ij}{)  


Kh4wQ  

    Test t = new Test();
nCc`y_:53  

  现在有两个线程都执行到t.test();方法.其中线程A获取了t的对象锁,进入test()方法内.
$Dv;{? A  

  这时x小于100,所以线程A进入等待.
&Qw4x.A{*  


vzv,}W  

  当一个线程调用了wait方法后,这个线程就进入了这个对象的休息室(waitset),这是一个虚拟的
vkIikfcGC  

对象,但JVM中一定存在这样的一个数据结构用来记录当前对象中有哪些程线程在等待.
Fv,@*z@ke  

  当一个线程进入等待时,它就会释放锁,让其它线程来获取这个锁.
(?J&@djT  


X#6._Y  

  所以线程B有机会获得了线程A释放的锁,进入test()方法,如果这时x还是小于100,线程B也进入了
@jzJO#e-‘  

t的休息室.
|#”v7B =V+  

  这两个线程只能等待其它线程调用notity[All]来唤醒.
“=^il{  


1<lNIW$  

  但是如果调用的是有参数的wait(time)方法,则线程A,B都会在休息室中等待这个时间后自动唤醒.
y.?Z!.  


q. >GTXj  


,?j3& 9g  

  [为什么真正的应用都是用while(条件)而不用if(条件)]
/n;k_/pJ  


.g-Jj  

  在实际的编程中我们看到大量的例子都是用    
uB6]r|:Qf  

        while(x < 100)
WPk;M~cDtF  

          wait();go();而不是用if,为什么呢?
‘lS`Gm=  

  在多个线程同时执行时,if(x <100)是不安全的.因为如果线程A和线程B都在t的休息室中等待,
a$JrUMK  

这时另一个线程使x==100了,并调用notifyAll方法,线程A继续执行下面的go().而它执行完成后,x有可能
*SA~BAIn  

又小于100,比如下面的程序中调用了–x,这时切换到线程B,线程B没有继续判断,直接执行go();就产生一个
It5,VB  

错误的条件,只有while才能保证线程B又继续检查一次.
5Jt=U].s  

 
H.Rc!!St}  


ao)@TX)<|  

  [notify/notifyAll方法]
pd&1O :y&+  

  这两个方法都是把某个对象上休息区内的线程唤醒,notify只能唤醒一个,但究竟是哪一个不能确
.;<>)  

定,而notifyAll则唤醒这个对象上的休息室中所有的线程.
t -(<.6  


pBRMvu  


NB4lAm+Y  

  一般有为了安全性,我们在绝对多数时候应该使用notifiAll(),除非你明确知道只唤醒其中的一个
w%!sx  

线程.
Z|pS0Jw  


s!dy+8`Go  

  那么是否是只要调用一个对象的wait()方法,当前线程就进入了这个对象的休息室呢?事实中,要调
5 /HWu-+{  

用一个对象的wait()方法,只有当前线程获取了这个对象的锁,换句话说一定要在这个对象的同步方法或
jD)qd^xD  

以这个对象为参数的同步块中.
lq:kMmtR  

 
Gfw%f<;  


:R4z=8O+  

class MyThread extends Thread{
qm(uB!Y.s  

Test t = new Test();
[<`hqT2:4  

  public void run(){
D`cVD  

    t.test();
wu/WEPL””  

    System.out.println(“Thread say:Hello,World!”);
}<iEpGES  

  }
oSq+9jw;K  

}
gn0%**Z  


~$</ ~c  


Co&<&74  

public class Test {
e8?G>_hw  


!Q]!O_-`  

  int x = 0;
R7U”;5Gb  

  public void test(){
‘a)9%Wa-d  

    if(x==0)
-1^m{,#Y  

    try{
`+b(0W  

      wait();
Y$”=IRiv#  

    }catch(Exception e){}
^|(*.?jH  

  }
(H]�VSv  

  public static void main(String[] args) throws Exception{
1hg7rxEM+  

    new MyThread().start();
SXD=AjwAc_  

  }
h~Sja(?p  

}
)I7joCU9t  


p .I�6|  

这个线程就不会进入t的wait方法而直接打印出Thread say:Hello,World!.
HgnET{ x  

而如果改成:
Zl lF”w  


Z!3%Ql|m  


`2&#He!EW  

public class Test {
&’l 8  


C13J| Bd  

  int x = 0;
NIj|i>  

  public synchornized void test(){
3m 7j5No  

    if(x==0)
d&[�r Z  

    try{
b_?G}?cj  

      wait();
(ha?AAL(  

    }catch(Exception e){}
Kv |Go  

  }
9~9cObcnI  

  public static void main(String[] args) throws Exception{
&)>F?_^  

    new MyThread().start();
_NOS ;<=  

  }
yE`Vv/j  

}
td� ‘  

我们就可以看到线程一直等待,注意这个线程进入等待后没有其它线程唤醒,除非强行退出
2OYc+/gz  

JVM环境,否则它一直等待.
^ j1G o$  


m5Y@,I^$  


HRBcgn1HV  

所以请记住:
~jOd%IPr  

[线程要想调用一个对象的wait()方法就要先获得该对象的监视锁,而一旦调用wait()后又立即
S ),{>5W  

释放该锁]
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/100534.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python基础知识点汇总

    python基础知识点汇总本文包括python基本知识:简单数据结构,数据结构类型(可变:列表,字典,集合,不可变:数值类型,字符串,元组),分支循环和控制流程,类和函数,文件处理和异常等等。(1)简单数据结构标识符第一个字符必须是字母表中字母或下划线_。 标识符的其他的部分由字母、数字和下划线组成。 标识符对大小写敏感。python中数字有四种类型:整数、布尔型、浮点数和复数。int(整数),如1,只有一种整数类型int,表示为长整型,没有python2中的Long。 bool(…

    2022年10月16日
    0
  • 整数补码加减法运算法则是什么_补码加减法中

    整数补码加减法运算法则是什么_补码加减法中整数的补码计算正数的补码计算:与原码相同负数的补码计算:①先求出负数的原码,如-4的原码为10000100②通过原码求出反码,负数的反码就是:除符号位以外,其他位全部取反,如-4的反码为11111011③负数的补码等于负数的反码末位+1,如-4的补码为11111100特例:约定-128的补码为10000000注:若已知补码求原码:正数也是它本身,负数的求法同上,即对补码除符号位外取反,末位加1,就得到原码整数补码加减运算加法[A+B]补=[A]补+[B]补减法[

    2022年9月22日
    0
  • 通过subString()方法来进行字符串截取

    通过subString()方法来进行字符串截取1.通过subString()方法来进行字符串截取。subString通过不同的参数来提供不同的截取方式1.1只传一个参数例如:Stringsb="bbbdsajjds";sb.substring(2);12将字符串从索引号为2开始截取,一直到字符串末尾。(索引值从0开始);1.2传入2个索引值Stringsb="bbbdsajjds";sb.substri…

    2022年5月24日
    39
  • Swoole的多进程模块[通俗易懂]

    Swoole的多进程模块

    2022年2月11日
    48
  • 【20】进大厂必须掌握的面试题-50个Hadoop面试

    【20】进大厂必须掌握的面试题-50个Hadoop面试

    2020年11月13日
    178
  • 解决删除symantec,需要输入密码的问题

    解决删除symantec,需要输入密码的问题安装了symantec后,感觉系统满了好多,决定卸载了。在卸载是发现需要输入密码,从来没有设置过密码,也不知是要输那个密码。研究了一阵子,可以通过以下步骤来绕过输入密码问题。1.安全模式启动,在命令行中输入serviecs.msc打开服务管理器,找到Symantec相关的项,在启动选项里设为禁用。其实发现真正要禁用的Symantecendpointprotecion,启动项是灰的,根本禁用不了。只能找到C:/\ProgramFiles\Symantec\Symantecendp

    2022年6月1日
    38

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号