Quantcast
Channel: C#
Viewing all articles
Browse latest Browse all 1853

多线程的死锁以及简单的线程间通信

$
0
0

 

一、死锁

当同步中嵌套同步时,就有可能出现死锁现象。

代码示例:

  1. //写一个死锁程序
  1. //定义一个类来实现Runnable,并复写run方法 
  2. class LockTest implements Runnable{ 
  3.   private boolean flag; 
  4.   LockTest(boolean flag) { 
  5.     this.flag=flag; 
  6.   } 
  7.   public void run(){ 
  8.     if(flag){ 
  9.       while(true) { 
  10.         synchronized(LockClass.locka) {//a锁 
  11.           System.out.println(Thread.currentThread().getName()+"------if_locka"); 
  12.                synchronized(LockClass.lockb) {//b锁
  13.  System.out.println(Thread.currentThread().getName()+"------if_lockb");
  14.                        } 
  15.         } 
  16.       } 
  17.     } 
  18.     else
  19.       while(true) { 
  20.         synchronized(LockClass.lockb) {//b锁 
  21.            System.out.println(Thread.currentThread().getName()+"------else_lockb"); 
  22.  
  23.            synchronized(LockClass.locka) { //a锁 
  24.                System.out.println(Thread.currentThread().getName()+"------else_locka"); 
  25.            } 
  26.         } 
  27.       } 
  28.     } 
  29.   } 
  30. //定义两个锁 
  31. class LockClass{ 
  32.   static Object locka = new Object(); 
  33.   static Object lockb = new Object(); 
  34. class DeadLock{ 
  35.   public static void main(String[] args) { 
  36.     //创建2个进程,并启动 
  37.     new Thread(new LockTest(true)).start(); //线程A
  38.     new Thread(new LockTest(false)).start();  //线程B 
  39.   } 

结果:程序卡住,不能继续执行

分析:两个线程A和B,A线程执行到第12行语句,线程A持有a锁,此时cpu转向执行线程B,线程B执行到21行语句,此时线程B持有b锁,因为线程A持有a锁,所以线程B不能再往下执行,同理A进程由于获取不到b锁,也不能往下执行,这就造成了死锁现象。

  

二、线程间的通信

其实就是多个线程在操作同一个资源,但是操作的动作不同

1、使用同步操作同一资源的示例

  1. /*
  1. 有一个资源
  2. 一个线程往里存东西,如果里边没有的话
  3. 一个线程往里取东西,如果里面有得话
  4. */  
  5. class Resource{ 
  6.   private String name; 
  7.   private String sex; 
  8.   private boolean flag=false
  9.   public synchronized void setInput(String name,String sex) { 
  10.     if(flag){ 
  11.       try{wait();}
  12.         catch(Exception e){}//如果有资源时,等待资源取出
  13.     } 
  14.     this.name=name; 
  15.     this.sex=sex; 
  16.     flag=true;//表示有资源 
  17.     otify();//唤醒等待 
  18.   } 
  19.   public synchronized void getOutput(){        
  20.     if(!flag) { 
  21.      try{wait();}catch(Exception e){}//如果木有资源,等待存入资源
  22.     } 
  23.     System.out.println("name="+name+"---sex="+sex);//这里用打印表示取出 
  24.     flag=false;//资源已取出 
  25.     notify();//唤醒等待 
  26.   } 
  27. //存线程 
  28. class Input implements Runnable{ 
  29.   private Resource r; 
  30.   Input(Resource r){ 
  31.     his.r=r; 
  32.   } 
  33.    public void run(){ //复写run方法 
  34.     int x=0; 
  35.     while(true) { 
  36.       if(x==0) { //交替打印张三和李四 
  37.         r.setInput("张三",".....男"); 
  38.       } 
  39.       else
  40.         r.setInput("丽丽","_____女"); 
  41.       } 
  42.       x=(x+1)%2;//控制交替打印 
  43.     } 
  44.   } 
  45. //取线程 
  46. class Output implements Runnable{ 
  47.   private Resource r; 
  48.   Output(Resource r){
  49.     this.r=r; 
  50.   } 
  51.   public void run(){  //复写run方法 
  52.     while(true) { 
  53.       r.getOutput(); 
  54.     } 
  55.   } 
  56. class ResourceDemo2{ 
  57.   public static void main(String[] args) { 
  58.     Resource r = new Resource();//表示操作的是同一个资源 
  59.        new Thread(new Input(r)).start();//开启存线程 
  60.       new Thread(new Output(r)).start();//开启取线程 
  61.   } 

几个小问题:

  1)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

    a,这些方法存在与同步中。

    b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。

    c,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

  2)wait(),sleep()有什么区别?

    wait():释放cpu执行权,释放锁。

    sleep():释放cpu执行权,不释放锁。

  3)为甚么要定义notifyAll?

    因为在需要唤醒对方线程时。如果只用notify,容易出现只唤醒本方线程的情况。导致程序中的所以线程都等待。

 

2JDK1.5中提供了多线程升级解决方案。

        将同步synchronized替换成显示的Lock操作。将Object中wait,notify,notifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。

代码示例:

  1. /*
  1. 生产者生产商品,供消费者使用
  2. 有两个或者多个生产者,生产一次就等待消费一次
  3. 有两个或者多个消费者,等待生产者生产一次就消费掉
  4.  
  5. */ 
  6. import java.util.concurrent.locks.*;
  7. class Resource{    
  8.   private String name; 
  9.   private int count=1; 
  10.   private boolean flag = false
  11.   //多态 
  12.   private Lock lock=new ReentrantLock(); 
  13.   //创建两Condition对象,分别来控制等待或唤醒本方和对方线程 
  14.   Condition condition_pro=lock.newCondition(); 
  15.   Condition condition_con=lock.newCondition(); 
  16.   //p1、p2共享此方法 
  17.   public void setProducer(String name)throws InterruptedExceptio{
  18.        lock.lock();//锁 
  19.      try
  20.        while(flag)//重复判断标识,确认是否生产 
  21.          condition_pro.await();//本方等待 
  22.        this.name=name+"......"+count++;//生产 
  23.              System.out.println(Thread.currentThread().getName()+"...生产..."+this.name);//打印生产 
  24.      flag=true;//控制生产\消费标识 
  25.      condition_con.signal();//唤醒对方 
  26.      } 
  27.      finally
  28.        lock.unlock();//解锁,这个动作一定执行 
  29.      } 
  30.    } 
  31.   //c1、c2共享此方法 
  32.    public void getConsumer()throws InterruptedException{ 
  33.      lock.lock(); 
  34.      try
  35.        while(!flag)//重复判断标识,确认是否可以消费 
  36.        condition_con.await(); 
  37.            System.out.println(Thread.currentThread().getName()+".消费."+this.name);//打印消费 
  38.        flag=false;//控制生产\消费标识 
  39.        condition_pro.signal(); 
  40.      } 
  41.      finally
  42.        lock.unlock(); 
  43.      } 
  44.   } 
  45. //生产者线程 
  46. class Producer implements Runnable{ 
  47.   private Resource res; 
  48.   Producer(Resource res) { 
  49.     this.res=res; 
  50.   } 
  51.   //复写run方法 
  52.   public void run(){ 
  53.     while(true) { 
  54.       try
  55.         res.setProducer("商品"); 
  56.       } 
  57.       catch (InterruptedException e) { 
  58.       } 
  59.     } 
  60.   } 
  61. }  
  62. //消费者线程 
  63. class Consumer implements Runnable{ 
  64.   private Resource res; 
  65.   Consumer(Resource res) { 
  66.     this.res=res; 
  67.   } 
  68.   //复写run 
  69.   public void run(){ 
  70.     while(true) { 
  71.       try
  72.         res.getConsumer(); 
  73.       } 
  74.       catch (InterruptedException e) { 
  75.       } 
  76.     } 
  77.   } 
  78. }
  79. class  ProducerConsumer{ 
  80.   public static void main(String[] args) { 
  81.     Resource res=new Resource(); 
  82.       new Thread(new Producer(res)).start();//第一个生产线程 p1 
  83.     new Thread(new Consumer(res)).start();//第一个消费线程 c1 
  84.       new Thread(new Producer(res)).start();//第二个生产线程 p2 
  85.     new Thread(new Consumer(res)).start();//第二个消费线程 c2 
  86.   } 
  87. }      

三、停止线程

  在JDK 1.5版本之前,有stop停止线程的方法,但升级之后,此方法已经过时。

那么现在我们该如果停止线程呢?

  只有一种办法,那就是让run方法结束。

1、开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

  如:run方法中有如下代码,设置一个flag标记。 

  1. public  void run(){ 
  1.   while(flag) {    
  2.     System.out.println(Thread.currentThread().getName()+"....run");  } 

   那么只要在主函数或者其他线程中,在该线程执行一段时间后,将标记flag赋值false,该run方法就会结束,线程也就停止了。

2、上面的1方法可以解决一般情况,但是有一种特殊情况:就是当线程处于冻结状态。就不会读取到标记。那么线程就不会结束。

  当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法interrupt();

如:

  1. class StopThread implements Runnable{ 
  2.   private boolean flag =true;
  3.   public  void run(){ 
  4.     while(flag) { 
  5.     System.out.println(Thread.currentThread().getName()+"....run");
  6.       } 
  7.   } 
  8.   public void changeFlag(){ 
  9.     flag = false
  10.   } 
  11. class  StopThreadDemo{ 
  12.   public static void main(String[] args) { 
  13.     StopThread st = new StopThread(); 
  14.     Thread t1 = new Thread(st); 
  15.     Thread t2 = new Thread(st);  
  16.     t1.start(); 
  17.     t2.start();  
  18.       int num = 0; 
  19.     while(true) { 
  20.       if(num++ == 60) { 
  21.         t1.interrupt();//清除冻结状态 
  22.         t2.interrupt(); 
  23.         st.changeFlag();//改变循环标记 
  24.         break
  25.          } 
  26.         System.out.println(Thread.currentThread().getName()+"......."+num); 
  27.     } 
  28.     System.out.println("over"); 
  29.   } 
  30. }  

扩展小知识:

1、join方法

  当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。

2、setPriority()方法用来设置优先级

  MAX_PRIORITY 最高优先级10

  MIN_PRIORITY   最低优先级1

  NORM_PRIORITY 分配给线程的默认优先级

3、yield()方法可以暂停当前线程,让其他线程执行。

Immagine icona: 

  • Threading
  • Compilatori
  • C#
  • Sviluppatori
  • Sviluppatori Intel AppUp®
  • Studenti
  • Area tema: 

    IDZone

    Includere in RSS: 

    1

    Viewing all articles
    Browse latest Browse all 1853

    Trending Articles



    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>