java并发的处理方式.docx

上传人:b****1 文档编号:80171 上传时间:2023-04-28 格式:DOCX 页数:8 大小:17.94KB
下载 相关 举报
java并发的处理方式.docx_第1页
第1页 / 共8页
java并发的处理方式.docx_第2页
第2页 / 共8页
java并发的处理方式.docx_第3页
第3页 / 共8页
java并发的处理方式.docx_第4页
第4页 / 共8页
java并发的处理方式.docx_第5页
第5页 / 共8页
java并发的处理方式.docx_第6页
第6页 / 共8页
java并发的处理方式.docx_第7页
第7页 / 共8页
java并发的处理方式.docx_第8页
第8页 / 共8页
亲,该文档总共8页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

java并发的处理方式.docx

《java并发的处理方式.docx》由会员分享,可在线阅读,更多相关《java并发的处理方式.docx(8页珍藏版)》请在冰点文库上搜索。

java并发的处理方式.docx

java并发的处理方式

java并发的处理方式

1什么是并发问题。

多个进程或线程同时(或着说在同一段时间内)访问同一资源会产生并发问题。

银行两操作员同时操作同一账户就是典型的例子。

比如A、B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户减去50元,A先提交,B后提交。

最后实际账户余额为1000-50=950元,但本该为1000+100-50=1050。

这就是典型的并发问题。

如何解决?

可以用锁。

用法1publicclassTest{

publicsynchronizedvoidprint(){

....;

}

}

某线程执行print()方法,则该对象将加锁。

其它线程将无法执行该对象的所有synchronized块。

用法2publicclassTest{

publicvoidprint(){

synchronized(this){//锁住本对象

...;

}

}

}

同用法1,但更能体现synchronized用法的本质。

用法3publicclassTest{

privateStringa="test";

publicvoidprint(){

synchronized(a){//锁住a对象

...;

}

}

publicsynchronizedvoidt(){

...;//这个同步代码块不会因为print()而锁定.

}

}

执行print(),会给对象a加锁,注意不是给Test的对象加锁,也就是说Test对象的其它synchronized方法不会因为print()而被锁。

同步代码块执行完,则释放对a的锁。

为了锁住一个对象的代码块而不影响该对象其它synchronized块的高性能写法:

publicclassTest{

privatebyte[]lock=newbyte[0];

publicvoidprint(){

synchronized(lock){

...;

}

}

publicsynchronizedvoidt(){

...;

}

}

静态方法的锁publicclassTest{

publicsynchronizedstaticvoidexecute(){

...;

}

}

效果同publicclassTest{

publicstaticvoidexecute(){

synchronized(TestThread.class){

...;

}

}

}3Java中的锁与排队上厕所。

锁就是阻止其它进程或线程进行资源访问的一种方式,即锁住的资源不能被其它请求访问。

在JAVA中,sychronized关键字用来对一个对象加锁。

比如:

publicclassMyStack{

intidx=0;

char[]data=newchar[6];publicsynchronizedvoidpush(charc){

data[idx]=c;

idx++;

}publicsynchronizedcharpop(){

idx--;

returndata[idx];

}publicstaticvoidmain(Stringargs[]){

MyStackm=newMyStack();

/**

下面对象m被加锁。

严格的说是对象m的所有synchronized块被加锁。

如果存在另一个试图访问m的线程T,那么T无法执行m对象的push和

pop方法。

*/

m.pop();//对象m被加锁。

}

}

Java的加锁解锁跟多个人排队等一个公共厕位完全一样。

第一个人进去后顺手把门从里面锁住,其它人只好排队等。

第一个人结束后出来时,门才会打开(解锁)。

轮到第二个人进去,同样他又会把门从里面锁住,其它人继续排队等待。

用厕所理论可以很容易明白:

一个人进了一个厕位,这个厕位就会锁住,但不会导致另一个厕位也被锁住,因为一个人不能同时蹲在两个厕位里。

对于Java就是说:

Java中的锁是针对同一个对象的,不是针对class的。

看下例:

MyStatckm1=newMyStack();

MyStatckm2=newMystatck();

m1.pop();

m2.pop();

m1对象的锁是不会影响m2的锁的,因为它们不是同一个厕位。

就是说,假设有3线程t1,t2,t3操作m1,那么这3个线程只可能在m1上排队等,假设另2个线程t8,t9在操作m2,那么t8,t9只会在m2上等待。

而t2和t8则没有关系,即使m2上的锁释放了,t1,t2,t3可能仍要在m1上排队。

原因无它,不是同一个厕位耳。

Java不能同时对一个代码块加两个锁,这和数据库锁机制不同,数据库可以对一条记录同时加好几种不同的锁,请参见:

4何时释放锁?

一般是执行完毕同步代码块(锁住的代码块)后就释放锁,也可以用wait()方式半路上释放锁。

wait()方式就好比蹲厕所到一半,突然发现下水道堵住了,不得已必须出来站在一边,好让修下水道师傅(准备执行notify的一个线程)进去疏通马桶,疏通完毕,师傅大喊一声:

"已经修好了"(notify),刚才出来的同志听到后就重新排队。

注意啊,必须等师傅出来啊,师傅不出来,谁也进不去。

也就是说notify后,不是其它线程马上可以进入封锁区域活动了,而是必须还要等notify代码所在的封锁区域执行完毕从而释放锁以后,其它线程才可进入。

这里是wait与notify代码示例:

publicsynchronizedcharpop(){

charc;

while(buffer.size()==0){

try{

this.wait();//从厕位里出来

}catch(InterruptedExceptione){

//ignoreit...

}

}

c=((Character)buffer.remove(buffer.size()-1)).

charValue();

returnc;

}publicsynchronizedvoidpush(charc){

this.notify();//通知那些wait()的线程重新排队。

注意:

仅仅是通知它们重新排队。

CharactercharObj=newCharacter(c);

buffer.addElement(charObj);

}//执行完毕,释放锁。

那些排队的线程就可以进来了。

再深入一些。

由于wait()操作而半路出来的同志没收到notify信号前是不会再排队的,他会在旁边看着这些排队的人(其中修水管师傅也在其中)。

注意,修水管的师傅不能插队,也得跟那些上厕所的人一样排队,不是说一个人蹲了一半出来后,修水管师傅就可以突然冒出来然后立刻进去抢修了,他要和原来排队的那帮人公平竞争,因为他也是个普通线程。

如果修水管师傅排在后面,则前面的人进去后,发现堵了,就wait,然后出来站到一边,再进去一个,再wait,出来,站到一边,只到师傅进去执行notify.这样,一会儿功夫,排队的旁边就站了一堆人,等着notify.终于,师傅进去,然后notify了,接下来呢?

1.有一个wait的人(线程)被通知到。

2.为什么被通知到的是他而不是另外一个wait的人?

取决于JVM.我们无法预先

判断出哪一个会被通知到。

也就是说,优先级高的不一定被优先唤醒,等待

时间长的也不一定被优先唤醒,一切不可预知!

(当然,如果你了解该JVM的

实现,则可以预知)。

3.他(被通知到的线程)要重新排队。

4.他会排在队伍的第一个位置吗?

回答是:

不一定。

他会排最后吗?

也不一定。

但如果该线程优先级设的比较高,那么他排在前面的概率就比较大。

5.轮到他重新进入厕位时,他会从上次wait()的地方接着执行,不会重新执行。

恶心点说就是,他会接着拉巴巴,不会重新拉。

6.如果师傅notifyAll().则那一堆半途而废出来的人全部重新排队。

顺序不可知。

JavaDOC上说,Theawakenedthreadswillnotbeabletoproceeduntilthecurrentthreadrelinquishesthelockonthisobject(当前线程释放锁前,唤醒的线程不能去执行)。

这用厕位理论解释就是显而易见的事。

5Lock的使用用synchronized关键字可以对资源加锁。

用Lock关键字也可以。

它是JDK1.5中新增内容。

用法如下:

classBoundedBuffer{

finalLocklock=newReentrantLock();

finalConditionnotFull=lock.newCondition();

finalConditionnotEmpty=lock.newCondition();finalObject[]items=newObject[100];

intputptr,takeptr,count;publicvoidput(Objectx)throwsInterruptedException{

lock.lock();

try{

while(count==items.length)

notFull.await();

items[putptr]=x;

if(++putptr==items.length)putptr=0;

++count;

notEmpty.signal();

}finally{

lock.unlock();

}

}publicObjecttake()throwsInterruptedException{

lock.lock();

try{

while(count==0)

notEmpty.await();

Objectx=items[takeptr];

if(++takeptr==items.length)takeptr=0;

--count;

notFull.signal();

returnx;

}finally{

lock.unlock();

}

}

}

(注:

这是JavaDoc里的例子,是一个阻塞队列的实现例子。

所谓阻塞队列,就是一个队列如果满了或者空了,都会导致线程阻塞等待。

Java里的ArrayBlockingQueue提供了现成的阻塞队列,不需要自己专门再写一个了。

)一个对象的lock.lock()和lock.unlock()之间的代码将会被锁住。

这种方式比起synchronize好在什么地方?

简而言之,就是对wait的线程进行了分类。

用厕位理论来描述,则是那些蹲了一半而从厕位里出来等待的人原因可能不一样,有的是因为马桶堵了,有的是因为马桶没水了。

通知(notify)的时候,就可以喊:

因为马桶堵了而等待的过来重新排队(比如马桶堵塞问题被解决了),或者喊,因为马桶没水而等待的过来重新排队(比如马桶没水问题被解决了)。

这样可以控制得更精细一些。

不像synchronize里的wait和notify,不管是马桶堵塞还是马桶没水都只能喊:

刚才等待的过来排队!

假如排队的人进来一看,发现原来只是马桶堵塞问题解决了,而自己渴望解决的问题(马桶没水)还没解决,只好再回去等待(wait),白进来转一圈,浪费时间与资源。

Lock方式与synchronized对应关系:

LockawaitsignalsignalAllsynchronizedwaitnotifynotifyAll

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 物理

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2