java学习笔记5

本文深入探讨了Java中线程间的死锁现象及其解决方案,通过具体代码示例展示了如何避免死锁,并详细解释了Java实现线程间通信的机制,包括wait、notify和notifyAll方法的应用。此外,文章还对线程等待和唤醒的过程进行了直观的说明,旨在帮助开发者理解和掌握线程通信的关键原理。

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

(1)   死锁

有多个进程, 且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其它进程或线程才可以执行的操

作,那么就称它们被死锁了。要避免死锁, 应该确保在获取多个锁时,在所有的线程中都以相同的顺序获取锁。在下面的例子中, 程序创建了两个类 A 和 B,它们分别具有方法 funA()和 funB(),在调用对方的方法前,funA()和 funB()都睡眠一会儿。主类 DeadLockDemo 创建 A 和B 实例,然后,产生第二个线程以构成死锁条件。funA()和 funB()使用 sleep()方法来强制死锁条件出现。

public class A {
       synchronizedvoid funA( B b)
         {
          String name =Thread.currentThread().getName();
          System.out.println(name+"进入A.foo");
          try {
                Thread.sleep(1000);
            } catch (Exception e)
           {
               System.out.println( e.getMessage() );
           }
              System.out.println(name+"调用B类中的last方法");
              b.last();
         }
       synchronizedvoid last()
         {
          System.out.println("A 类中的last()方法");    
         }
}
public class B {
       synchronizedvoid funB(A a)
       {
     String name = Thread.currentThread().getName();
     System.out.println(name+"进入B类中的");
     try {
              Thread.sleep(1000);
           } catch (Exception e)
        {
                System.out.println( e.getMessage() );
           }
     System.out.println(name+"调用A类中的last()方法");
     a.last();
       }
       synchronizedvoid last()
        {
         System.out.println("B类中的last()方法");    
        }
}
public class DeadLockDemo implementsRunnable {
       Aa = new A();
       B  b = new B();
       DeadLockDemo()
       {
        Thread.currentThread().setName("main-->>thread");
        new Thread(this).start();     
        a.funA(b);   
        System.out.println("main线程运行完毕!");
       }
       publicvoid run()
        {
              Thread.currentThread().setName("test-->>Thread");
              b.funB(a);
              System.out.println("其他线程运行完毕");
        }
  public  static void main(String []args)
   {
          new DeadLockDemo(); 
   }
}

从运行结果可以发现,Test-->> Thread 进入了 b 的监视器,然后又在等待 a 的监视器。同时 Main-->> Thread 进入了 a 的监视器并等待 b 的监视器。这个程序永远不

会完成。

(2)   线程间通讯

代码示例:

public class Producer implements Runnable
{
   Pq = null;
  public Producer(P q)
    {
          this.q = q;
    }
       publicvoid run()
       {
    int i = 0;
     while( true )
     {
            if( i == 0)
              {
                   q.set("张三","男");
              }
            else
            {
                   q. set("李四","女");
            }
            i=( i + 1 ) % 2;
     }
       }
}
public class Consumer implements Runnable
  {
    P q =null;
   public Consumer( P q)
    {
    this.q = q;
    
    }
   public void run()
     {
           while( true )
             {
                  q.get();
             }
     }
  }
public class P {
       Stringname = "李四";
       Stringsex = "女";
       publicsynchronized void set(String name, String sex)
       {
              this.name= name;
              this.sex=sex;
       }
       publicsynchronized void get( )
       {
              System.out.println(this.name+ "------> "+ this.sex );
             
       }
      
}
public class ThreadCommunation {
       publicstatic void main(String[] args)
       {
              P  q = new P();
              newThread(new Producer(q) ).start();
              newThread (new Consumer(q) ).start();
       }
 
}


运行发现程序的输出结果是正确的,但是这里问题产生了,从程序的执行结果来看, Consumer 线程对 Producer 线程放入的一次数据连续读取了多次,并不符合实际的要求。实际要求的结果是,Producer 放一次数据,Consumer 就取一次;反之, Producer 也必须等到 Consumer 取完后才能放入新的数据, 而这一问题的解决就需要使用下面所要讲到的线程间的通信。

Java是如何实现通信的

Java 是通过 Object 类的 wait、 notify、 notifyAll

这几个方法来实现线程间的通信的,又因为所有的类都是从 Object 继承的,所以任何类都可以直接使用这些方法。下面是这三个方法的简要说明:wait:告诉当前线程放弃监视器并进入睡眠状态,直到其它线程进入同一监视器并调用 notify 为止。

notify:唤醒同一对象监视器中调用 wait 的第一个线程。类似排队买票,一个人买完之后,后面的人可以继续买。

notifyAll:唤醒同一对象监视器中调用 wait 的所有线程,具有最高优先级的线程首先被唤醒并执行。

通过对calss p 的改造完全满足所想

public class Producer implements Runnable
{
   Pq = null;
  public Producer(P q)
    {
          this.q = q;
    }
       publicvoid run()
       {
    int i = 0;
     while( true )
     {
            if( i == 0)
              {
                   q.set("张三","男");
              }
            else
            {
                   q. set("李四","女");
            }
            i=( i + 1 ) % 2;
     }
       }
}
public class Consumer implements Runnable
  {
    Pq =null;
   public Consumer( P q)
    {
    this.q = q;
    
    }
   public void run()
     {
           while( true )
             {
                  q.get();
             }
     }
  }
public class P {
       Stringname = "李四";
       Stringsex = "女";
       booleanbFull = false;
       publicsynchronized void set(String name, String sex)
       {
              if(bFull)
                {
                     try{
                            wait();
                        }catch(InterruptedException e)
                        {}
                }
              this.name= name;
              try{
                     Thread.sleep(10);
                 } catch (Exception e) {
                     System.out.println(e.getMessage() );
              }
              this.sex= sex;
              bFull= true;
              notify();
       }
       publicsynchronized void get( )
       {
              if(!bFull )
              {
                     try{
                   wait();
                  }catch (InterruptedException e)
                   {}
              }
              System.out.println(this.name+ "------> "+ this.sex );
              bFull= false;
              notify();
       }
      
}
public class ThreadCommunation {
       publicstatic void main(String[] args)
       {
              P  q = new P();
              newThread(new Producer(q) ).start();
              newThread (new Consumer(q) ).start();
       }
 
}


wait、notify、notifyAll 这三个方法只能在 synchronized 方法中调用,即无论线程调用一个对象的 wait 还是 notify 方法, 该线程必须先得到该对象的锁标记,这样, notify

只能唤醒同一对象监视器中调用wait 的线程,使用多个对象监视器,就可以分别有多个 wait、notify 的情况,同组里的 wait 只能被同组的 notify 唤醒。

线程的等待和唤醒过程如下图所示:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值