好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

多线程实现资源共享的问题学习与总结

多线程实现资源共享的问题学习与总结

多线程实现资源共享的问题学习与总结

我么知道Java传统 多线程的实现有两种方法 , 继承 Thread 类或者实现 Runnable 即可 . 线程启动时调用 start() 方法 .

实现 Runnable 接口相比继承 Thread 类有如下好处 :

1. 避免单继承的局限 , 一个类可以同时实现多个接口

2. 适合资源的共享 .

 

实现多线程模拟售票点卖票来说明实现 Runnable 即可可以达到资源共享的目的 .

使用继承 Thread 类的多线程售票实现

  package   org.dennist.thread.demo;
  /**  
 *
 *  TicketThread.java    
 *
 *    @version   : 1.1
 *  
 *    @author    : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *    @since       : 1.0        创建时间:    2013-2-24        下午02:22:49
 *     
 *  TODO     :    class TicketThread.java is used for ...
 *
   */ 
 public   class  TicketThreadT  extends   Thread{
    
      private   int  num = 5;         //  总共票数设定为5张 
     
    @Override
      public   void   run() {
          for ( int  i=0; i<10; i++ ){
              if ( this .num>0){         //  打印买票信息 
                System.out.println(Thread.currentThread().getName() + "买票: " +  this .num-- );
            }
        }
    }
    
      public   static   void   main(String[] args) {
        TicketThreadT th1  =  new  TicketThreadT();         //  线程一 
        th1.setName("售票口一" );    
        TicketThreadT th2  =  new  TicketThreadT();         //  线程二 
        th2.setName("售票口二" );
        TicketThreadT th3  =  new  TicketThreadT();         //  线程三 
        th3.setName("售票口三" );
        
          //  分别启动三个线程 
         th1.start();
        th2.start();
        th3.start();
    }
}  

程序运行结果:

总共 5 张票 , 启动了三个线程 , 从打印结果可以看出 , 一共卖出去了 15 张票 , 线程之间没有进行资源共享

 

实现 Runnable 的售票线程

 

  package   org.dennist.thread.demo;
  /**  
 *
 *  TicketThreadR.java    
 *
 *    @version   : 1.1
 *  
 *    @author    : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *    @since       : 1.0        创建时间:    2013-2-24        下午02:29:23
 *     
 *  TODO     :    class TicketThreadR.java is used for ...
 *
   */ 
 public   class  TicketThreadR  implements   Runnable{
    
      private   int  num = 5;             //  总共票数设定为5张 
     
    @Override
      public   void   run() {
          for ( int  i=0; i<10; i++ ){
              if ( this .num>0){             //  打印买票信息 
                System.out.println(Thread.currentThread().getName() + "买票: " +  this .num-- );
            }
        }
    }

      public   static   void   main(String[] args) {
        TicketThreadR ticketThread  =  new   TicketThreadR();
        
        Thread th1  =  new  Thread(ticketThread);     //  线程一 
        th1.setName("售票口一" );
        Thread th2  =  new  Thread(ticketThread);     //  线程二 
        th2.setName("售票口二" );
        Thread th3  =  new  Thread(ticketThread);     //  线程三 
        th3.setName("售票口三" );
        
        th1.start();
        th2.start();
        th3.start();
    }
}  

程序运行结果

虽然现在程序中有三个线程 , 但是三个线程总共卖出了 5 张票 , 也就是说使用 Runnable 实现的多线程可以达到资源共享的目的 .

J ava 多线程访问共享方式

(1) 如果每个线程执行的代码相同,可以使用同一个 Runnable 对象,这个 Runnable 对象中有那个共享数据,例如,买票系统就可以这么做。

(2) 如果每个线程执行的代码不同,这时候需要用不同的 Runnable 对象,有如下两种方式来实现这些 Runnable 对象之间的数据共享:

   1 、将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个 Runnable 对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

   2 、将这些 Runnable 对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个 Runnable 对象调用外部类的这些方法。

   3 、上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的 Runnable 对象作为外部类中的成员内部类或局部内部类。

   4 、总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。

(3) 极端且简单的方式,即在任意一个类中定义一个 static 的变量,这将被所有线程共享。

 

 

在 Thread 类中存在以下的几个方法可以设置和取得名字 .

设置名字 : public final void setName(String name) 

public Thread(Runnable target, String name)

public Thread(String name)

取得名字 : public final String getName()

在线程的操作中因为其操作的不确定性 , 所以提供了一个方法 , 可以取得当前的操作线程 .

public static Thread currentThread()

说明 :

对于线程的名字一般是在启动前进行设置 , 最好不要设置相同的名字 , 最好不要为一个线程改名字 .

在 Java 执行中一个 Java 程序至少启动 2 个线程 : 一个主线程和一个垃圾回收线程 .

 

多线程的同步问题

上面的实现 Runnable 程序就真的没问题了吗?我们知道现实生活中买票总会有等待 , 跟延迟 , 那么我们模拟现实生活中的买票然后再来看上面的程序输出 .

  package   org.dennist.thread.demo;
  /**  
 *
 *  TicketThreadR.java    
 *
 *    @version   : 1.1
 *  
 *    @author    : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *    @since       : 1.0        创建时间:    2013-2-24        下午02:29:23
 *     
 *  TODO     :    class TicketThreadR.java is used for ...
 *
   */ 
 public   class  TicketThreadR  implements   Runnable{
    
      private   int  num = 5;             //  总共票数设定为5张 
     
    @Override
      public   void   run() {
          for ( int  i=0; i<10; i++ ){
              try   {
                Thread.sleep( 200);     //  休息200毫秒 
            }  catch   (InterruptedException e) {
                e.printStackTrace();
            }        
              if ( this .num>0){             //  打印买票信息 
                System.out.println(Thread.currentThread().getName() + "买票: " +  this .num-- );
            }
        }
    }

      public   static   void   main(String[] args) {
        TicketThreadR ticketThread  =  new   TicketThreadR();
        
        Thread th1  =  new  Thread(ticketThread);     //  线程一 
        th1.setName("售票口一" );
        Thread th2  =  new  Thread(ticketThread);     //  线程二 
        th2.setName("售票口二" );
        Thread th3  =  new  Thread(ticketThread);     //  线程三 
        th3.setName("售票口三" );
        
        th1.start();
        th2.start();
        th3.start();
    }
}  

 

如果想解决这样的问题 , 就必须使用同步 , 所谓的同步就是指多个操作在同一个时间段内只有一个线程进行 , 其他线程要等待此线程完成之后才可以继续执行 .

可以通过同步代码的方法进行代码的加锁操作 , 同步的实现有 2 中方法 :

1. 同步代码块

2. 同步方法

 

同步代码块

使用 synchronized 关键字进行同步代码块的声明 , 但是在使用此操作时必须明确的指出到底要锁定的是哪个对象 , 一般是以当前对象为主 .

   synchronized( 对象 ){   // 一般都是讲 this 锁定

         // 锁定对象

     }

上面的问题使用同步代码块解决

  package   org.dennist.thread.demo;
  /**  
 *
 *  TicketThreadR.java    
 *
 *    @version   : 1.1
 *  
 *    @author    : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *    @since       : 1.0        创建时间:    2013-2-24        下午02:29:23
 *     
 *  TODO     :    class TicketThreadR.java is used for ...
 *
   */ 
 public   class  TicketThreadR  implements   Runnable{
    
      private   int  num = 5;             //  总共票数设定为5张 
     
    @Override
      public   void   run() {
          for ( int  i=0; i<10; i++ ){
              //  使用同步代码块 
             synchronized  ( this  ) {
                  try   {
                    Thread.sleep( 300);     //  休息300毫秒 
                }  catch   (InterruptedException e) {
                    e.printStackTrace();
                }        
                  if ( this .num>0 ){    
                      //  打印买票信息 
                    System.out.println(Thread.currentThread().getName() + "买票: " +  this .num-- );
                }
            }
            
        }
    }

      public   static   void   main(String[] args) {
        TicketThreadR ticketThread  =  new   TicketThreadR();
        
          new  Thread(ticketThread,"售票口一").start();     //  线程一 
         new  Thread(ticketThread,"售票口二").start();     //  线程二 
         new  Thread(ticketThread,"售票口三").start();     //  线程三 
     }
}  

 

同步方法

同步方法是在方法上增加 synchronized 关键字修饰
上面的问题使用同步代码块解决

  package   org.dennist.thread.demo;
  /**  
 *
 *  TicketThreadR.java    
 *
 *    @version   : 1.1
 *  
 *    @author    : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *    @since       : 1.0        创建时间:    2013-2-24        下午02:29:23
 *     
 *  TODO     :    class TicketThreadR.java is used for ...
 *
   */ 
 public   class  TicketThreadR  implements   Runnable{
    
      private   int  num = 5;             //  总共票数设定为5张 
     
    @Override
      public   void   run() {
          for ( int  i=0; i<10; i++ ){
            sale();                      //  调用同步方法 
         }
    }
    
      //  使用同步方法 
     public   synchronized   void   sale(){
          try   {
            Thread.sleep( 300);     //  休息300毫秒 
        }  catch   (InterruptedException e) {
            e.printStackTrace();
        }        
          if ( this .num>0 ){    
              //  打印买票信息 
            System.out.println(Thread.currentThread().getName() + "买票: " +  this .num-- );
        }
    }
    
      public   static   void   main(String[] args) {
        TicketThreadR ticketThread  =  new   TicketThreadR();
        
          new  Thread(ticketThread,"售票口一").start();     //  线程一 
         new  Thread(ticketThread,"售票口二").start();     //  线程一 
         new  Thread(ticketThread,"售票口三").start();     //  线程一 
     }
}  

 

多个线程共享同一资源的时候需要进行同步 , 但是过多的同步会造成死锁 .

 

什么叫死锁?死锁产生的主要原因是什么?死锁产生的必要条件 , 如何解决死锁 ?​

死锁指在多道程序系统中 , 一组进程中的每一个进程均无限期的等待该被改组进程中的另一个进程所以占有且永远不会释放的资源 , 这种现象称为系统处于死锁状态 .​

死锁产生的原因主要有 2 个 :​

  1. 竞争资源 , 系统提供的资源数量有限 , 不能满足每个进程的要求 ​

  2. 多道程序运行时 ,. 进程推进顺序不合理 ​

产生死锁的必要条件 ​

  1. 互斥使用资源 ​

  2. 占用并等待资源 ​

  3. 不可抢夺资源 ​

  4. 循环等待资源 ​

解决死锁的方法 ​

  1. 预防死锁 : 破坏死锁产生的条件 ( 除过互斥条件 , 因为破坏互斥条件不现实 )​

  2. 避免死锁 ​

  3. 检测与排除 ​

  4. 置之不理 ​

 

转载请注明出处:[ http://www.cnblogs.com/dennisit/archive/2013/02/24/2925288.html ]

热爱生活,热爱Coding,敢于挑战,用于探索 ...

 

分类:  JavaSE

标签:  多线程 ,  资源共享 ,  死锁 ,  死锁产生的原因 ,  避免死锁

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于多线程实现资源共享的问题学习与总结的详细内容...

  阅读:35次