Java多线程4—线程同步问题+火车票售票系统

时间:2020-12-19 17:33:37

        在上一篇文章中写到了许多线程共享同一数据,这种情况在现实的生活中也是经常发生的,比如火车站的火车票售票系统。火车票售票系统是一个常年运行的系统,为了满足乘客的需求,我们不能只设一个窗口,必须设很多的售票窗口,每个售票窗口就像一个线程,它们各自运行,共同访问相同的数据——火车票的数量,下面我们用多线程模仿一下火车票售票系统:

public

class TicketSystem

{

   public static void main(String[] args)

   {

      SellThread st=new SellThread();

      new Thread(st).start();new Thread(st).start();new Thread(st).start();

   }

}

 

class

SellThread implements Runnable

{

   int tickets=100;

   public void run()

   {

      while(true)

      {

         if(tickets>0)

         {

                System.out.println("obj:"+Thread.currentThread().getName()+" sell tickets:"+tickets);

                tickets--;

         }

      }

   }

}

         程序输出了线程0、1、2三个线程从第100张票卖到第1张票的过程,这个程序正确的输出了。但是实际情况中火车票售票系统是常年运行的,有可能第一顾客已经买到了最后一张票,但是执行到tickets--的时候,线程切换了,这时候另外一个顾客也买最后一张票,这时候系统显示还有票,这时候这个顾客也订票了,并且也在执行到tickets--的时候,线程切换。再回去执行原来的线程,前个顾客买到了最后一张票,后一个顾客但是却买到了第0张票。显然这个是不对的。下面我们修改上面程序模拟一下:

class

SellThread implements Runnable

{

   int tickets=100;

   public void run()

   {

      while(true)

      {

         if(tickets>0)

         {

            Try//让线程睡眠10毫秒,只是修改了这里

            {

                Thread.sleep(10);

            } catch (InterruptedExceptione)

            {

                e.printStackTrace();

            }

            System.out.println("obj:"+Thread.currentThread().getName()+" sell tickets:"+tickets);

            tickets--;

         }

      }

   }

}

结果的一部分如下:

obj:Thread-0 sell tickets:2

obj:Thread-2 sell tickets:2

obj:Thread-1 sell tickets:0

obj:Thread-0 sell tickets:-1

        我们可以看出上面程序两次售出了第二张票,并且卖出了第0张和-1张票,显然这个结果是不对的。遇到了不对的情况,我们就应该得想办法解决它,在Java中运用同步的方法解决这个问题。

        在介绍之前我们先说一下临界区,在Java中代码段访问了同一个对象或数据,那么这个代码段就叫做临界区。上面程序中run()方法中循环里面的代码就叫做临界区。我们需要对这个临界区进行保护,这样就用到了线程的同步。

       在Java中线程的同步有两种方法,一种是同步块,另一种是同步方法。不管是哪种方法都用到了synchronized关键字。下面我们就修改上面的程序用实例显示一下:

                  class SellThread implementsRunnable

{

      int tickets=100;

      Object obj = new Object();

      public void run()

      {

         while(true)

         {

            synchronized(obj)//加上一个同步关键字

            {

               if(tickets>0)

               {

                  try{

                     Thread.sleep(10);

                  } catch (InterruptedExceptione)

                  {

                     e.printStackTrace();

                  }

System.out.println("obj:"+Thread.currentThread().getName()+" sell tickets:"+tickets);

                  tickets--;

                  }

               }

            }

         }

}

        上面使用synchronized关键字的时候需要一个对象,这里的对象可以是任意对象,我们在这里选择的是Object对象,你也可以选择String类的对象。每一个对象都有一个监视器或者叫锁。当我将obj作为参数传给synchronized关键字的时候,就相当于给这段代码加了一个锁,当我执行到这段代码的时候,先判断是否加锁,如果没有加锁,先将其加锁然后执行代码。如果加锁了只能等待。等到给代码加锁的线程执行完成之后,会将锁打开。

下面我们介绍同步方法,我们还是以程序的形式给出:

         class SellThread implements Runnable

         {

            int tickets=100;

            Object obj = new Object();

            public synchronized void sell()//用关键字sychronized                                 //关键字标志同步方法

            {

                  if(tickets>0)

                  {

                     try

                     {

                        Thread.sleep(10);

                     }

                     catch(Exception e)

                     {

                        e.printStackTrace();

                     }

                                 

   System.out.println("sell():"+Thread.currentThread().getName()+

                         " sell tickets:"+tickets);

                        tickets--;

                  }

               }

           

         public void run()

         {

            while(true)

            {

               sell();

            }

         }

}

        上面的程序用sychronized关键字直接标志的是sell()方法,我们在run()方法里调用sell()的时候,sell()方法中的代码就相当于一个整体都一起执行。但是同步方法也是使用加锁的方法进行同步的,它不像同步块那样传递一个对象,那么他是对哪个对象加的锁呢?同步方法是对程序中的this对象加锁以实现同步的。

我们有时候会写一些静态的方法,这些方法也必须同步。静态方法只属于类本身,没有this对象,那么它对谁加锁呢?前面我们介绍过每一个类都有一个Class类的对象(详细介绍请看:http://blog.csdn.net/mengxiangyue/article/details/6831820),静态方法正是对这个Class类的对象进行加锁以实现同步方法的。

        对于同步方法就先介绍到这里,如果有错请大家指出,希望对大家有帮助。