java线程同步(synchronized,wait,notify,notifyAll)

本文深入探讨了并发控制及线程协作中的关键概念,通过实例解析了多线程环境下同步问题,特别是针对线程间的等待、通知机制以及如何避免死锁现象。详细介绍了如何通过修改代码逻辑来确保线程按预期顺序执行任务,并成功打印出预定序列,从而解决了多线程同步问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

synchronized:

包括synchronized方法和synchronized块。

synchronized方法使用this作为默认的“同步监视器”,而synchronized块则需自行指定。

wait、notify、notifyAll必须在同步方法或块中,否则会抛出异常

Object.wait:

要执行obj.wait,当前线程必须拥有obj的监视器(monitor)

执行后会导致当前线程进入等待,直到其它线程调用obj.notify或obj.notifyAll

执行后当前线程会放弃在obj上的所有同步请求(synchronization claims)

注意只是使当前线程处于等待,但对同步方法/块的占有却释放了,所以其它线程便可以随后访问这个同步方法/块了

Object.notify,Object.notifyAll:

要执行obj.notify、obj.notifyAll,当前线程必须拥有obj的监视器(monitor)

obj.notify会随机唤醒一个等待在obj上的线程,而obj.notifyAll却会唤醒所有等待在obj上的线程

被唤醒的线程不能马上执行,直到当前线程放弃obj的同步锁(The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. ),也就是当前线程离开了同步方法/块后


实战一下吧:创建三个线程(a线程打印字符a,b线程打印字符b,c线程打印字符c),使三个线程协作打印出abcabcabc。

[java] view plain copy
  1. classPrinterextendsThread{
  2. privateStringchr;
  3. privateObjectpre,curr;
  4. publicPrinter(Objectpre,Objectcurr,Stringchr){
  5. super();
  6. this.pre=pre;
  7. this.curr=curr;
  8. this.chr=chr;
  9. }
  10. @Override
  11. publicvoidrun(){
  12. for(inti=0;i<3;i++){
  13. synchronized(pre){
  14. synchronized(curr){
  15. System.out.print(chr);
  16. curr.notify();
  17. }
  18. try{
  19. pre.wait();
  20. }catch(InterruptedExceptione){
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25. System.out.print("\n"+chr+"end");
  26. }
  27. }
  28. publicclassTest{
  29. publicstaticvoidmain(String[]args)throwsInterruptedException{
  30. Object
  31. a=newObject(),
  32. b=newObject(),
  33. c=newObject();
  34. Printer
  35. printerA=newPrinter(c,a,"a"),
  36. printerB=newPrinter(a,b,"b"),
  37. printerC=newPrinter(b,c,"c");
  38. printerA.start();
  39. Thread.sleep(2000);
  40. printerB.start();
  41. Thread.sleep(2000);
  42. printerC.start();
  43. }
  44. }

这个例子你可能看到过,是的,我也是网上发现的。但是你发现没,最终结果是:

abcabc
aend

bend(未出现)

cend(未出现)

而且程序并没有退出!为什么?

为简化问题,我们不妨把for循环去掉,原理是一样的

[java] view plain copy
  1. @Override
  2. publicvoidrun(){
  3. synchronized(pre){
  4. synchronized(curr){
  5. System.out.print(chr);
  6. curr.notify();
  7. }
  8. try{
  9. pre.wait();
  10. }catch(InterruptedExceptione){
  11. e.printStackTrace();
  12. }
  13. }
  14. System.out.print("\n"+chr+"end");
  15. }

a线程执行后打印“a”,唤醒等待在a上的线程(此时其实并没有其它线程在a上等待)并在pre.wait处等待

b线程执行后打印“b”,唤醒等待在b上的线程(此时其实并没有其它线程在b上等待)并在pre.wait处等待

c线程执行后打印“c”,唤醒等待在c上的线程并在pre.wait处等待。请注意,等待在c上的线程被唤醒了,因此a线程继续往下执行,打印出“aend”后a线程结束。但问题是b,c线程呢?他们并没有被唤醒,因此它们始终在pre.wait处等待着!


因此我们必须为b,c设置一个出口:当i=2(其实是循环次数-1即3-1=2)的时候不执行pre.wait

[java] view plain copy
  1. classPrinterextendsThread{
  2. privateStringchr;
  3. privateObjectpre,curr;
  4. publicPrinter(Objectpre,Objectcurr,Stringchr){
  5. super();
  6. this.pre=pre;
  7. this.curr=curr;
  8. this.chr=chr;
  9. }
  10. @Override
  11. publicvoidrun(){
  12. for(inti=0;i<3;i++){
  13. synchronized(pre){
  14. synchronized(curr){
  15. System.out.print(chr);
  16. curr.notify();
  17. }
  18. if(i<2){
  19. try{
  20. pre.wait();
  21. }catch(InterruptedExceptione){
  22. e.printStackTrace();
  23. }
  24. }
  25. }
  26. }
  27. System.out.print("\n"+chr+"end");
  28. }
  29. }
  30. publicclassTest{
  31. publicstaticvoidmain(String[]args)throwsInterruptedException{
  32. Object
  33. a=newObject(),
  34. b=newObject(),
  35. c=newObject();
  36. Printer
  37. printerA=newPrinter(c,a,"a"),
  38. printerB=newPrinter(a,b,"b"),
  39. printerC=newPrinter(b,c,"c");
  40. printerA.start();
  41. Thread.sleep(2000);
  42. printerB.start();
  43. Thread.sleep(2000);
  44. printerC.start();
  45. }
  46. }

当i=2时跳过pre.wait,程序继续执行。之后i=3,由于此时i已经不满足for循环的要求,所以循环结束,直接执行System.out.print("\n"+chr+"end"),之后线程退出。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值