阳光VIP

少壮不努力,老大徒伤悲。平日弗用功,自到临期悔。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

下面的程序会逐步引出synchronized的用法:
[例]

class TicketMan
{
 public static void main(String[]aresg)
 {
  Ticket t=new Ticket();
  //创建4个线程
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
 }
}

class Ticket implements Runnable
{
 int tk=10;
 public void run()
 {
  while(tk>0)
  {
   System.out.println(Thread.currentThread().getName()+"窗口正在售出"+tk--+"号票.");
  }
 }
}
//输出结果:4个窗口,成功把10张票随机分配售完.
/*
Thread-0窗口正在售出10号票.
Thread-1窗口正在售出7号票.
Thread-1窗口正在售出5号票.
Thread-3窗口正在售出8号票.
Thread-2窗口正在售出9号票.
Thread-3窗口正在售出3号票.
Thread-3窗口正在售出1号票.
Thread-1窗口正在售出4号票.
Thread-0窗口正在售出6号票.
Thread-2窗口正在售出2号票.
*/

结论:
使用Runnable接口创建多线程,适合多个相同的程序代码的线程去处理分享同一个资源的情况,把虚拟CPU(线程)同程序的代码数据有效分离,较好体现了面向对象的设计思想.
【多线程安全问题考虑】
程序像上面那样写并不安全,输出的结果有可能输出“售出负号票”,理由:假设Thread-0线程执行到while语句里时,这时恰巧tk=1;突然停了.切换到Thread-1线程,也是运行到while语句里突然切换到了Thread-2线程,这样while语句里有三个线程有待执行,tk=1,等这三个线程执行完毕tk就变成负数了.下面我们用sleep()来模拟这个假设:

class TicketMan
{
 public static void main(String[]aresg)
 {
  Ticket t=new Ticket();
  //创建4个线程
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
 }
}

class Ticket implements Runnable
{
 int tk=10;
 public void run()
 {
  while(tk>0)
  {
   try
   {
     Thread.sleep(1);//当前线程暂停1毫秒,处理器会去执行其他线程. 
   }
   catch (Exception ex)
   {
     ex.printStackTrace();//输出造成异常更为详细的信息.
   }
   System.out.println(Thread.currentThread().getName()+"窗口正在售出"+tk--+"号票.");
  }
 }
}
//输出结果:
/*
Thread-0窗口正在售出9号票.
Thread-2窗口正在售出7号票.
Thread-3窗口正在售出8号票.
Thread-1窗口正在售出10号票.
Thread-2窗口正在售出5号票.
Thread-0窗口正在售出6号票.
Thread-2窗口正在售出2号票.
Thread-1窗口正在售出3号票.
Thread-3窗口正在售出4号票.
Thread-2窗口正在售出0号票.
Thread-0窗口正在售出1号票.
*/

多次运行TicketMan类调试,结果都不一样,有时出现0,有时不出现,模拟的程序已经达到要求了.那么针对这个卖票程序,到底怎样写才安全呢?这就是即将要引出的线程同步问题:synchronized关键字,代表这个方法枷锁,一次只能允许一个线程通过.比如线程A执行到synchronized代码快或方法体时,首先的工作就是检测有没有其他线程在执行该语句,有的话,就等其他线程出来后,再进去.
synchronized代码块的用法-synchronized(Object){}举例如下:

class TicketMan
{
 public static void main(String[]aresg)
 {
  Ticket t=new Ticket();
  //创建4个线程
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
 }
}

class Ticket implements Runnable
{
 int tk=10;
 String s=new String("");
 public void run()
 {
  //run()方法体里千万别重置s对象,不然会放进很多线程进来,那就失去本来意义了,不妨把String s=new String("")移到run方法里试试看;.
  while(tk>0)
  {

  //synchronized代码块开始
  synchronized(s)
  {
     //注意: synchronized()方法体里千万别重置s对象.
     if(tk>0)
     {
        try
        {
          Thread.sleep(50);//当前线程暂停1毫秒,处理器会去执行其他线程. 
        }
        catch (Exception ex)
        {
          ex.printStackTrace();//输出造成异常更为详细的信息.
        }

        System.out.println(Thread.currentThread().getName()+"窗口正在售出"+tk--+"号票.");
     }
   }
  //synchronized代码块结束

  }
 }
}

synchronized方法的用法需要与run()方法两个结合起来用:

/*
语法:
public synchronized void duMuQiao()
{}

public void run()
{
  .....
  调用synchronized修改过的方法:duMuQiao();
  .....
}
举例如下:
*/
class TicketMan
{
 public static void main(String[]aresg)
 {
  Ticket t=new Ticket();
  //创建4个线程
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
  new Thread(t).start();
 }
}

class Ticket implements Runnable
{
 int tk=10;

 public void run()
 {
  while(tk>0)
  {
   duMuQiao();//调用synchronized修改过的方法:duMuQiao();
  }
 }

  //synchronized方法开始
  public synchronized void duMuQiao()
  //一个线程进入这个方法后 这个方法的大门就会暂时关闭(不许其他线程进入)直到这个线程走出这个方法后,该方法的大门才会敞开.
  {

     if(tk>0)
     {
        try
        {
          Thread.sleep(50);//当前线程暂停1毫秒,处理器会去执行其他线程. 
        }
        catch (Exception ex)
        {
          ex.printStackTrace();//输出造成异常更为详细的信息.
        }

        System.out.println(Thread.currentThread().getName()+"窗口正在售出"+tk--+"号票.");
     }
   }
  //synchronized方法结束
}
CSDN
Java编程技术交流QQ群:171396965  技术需要交流,技术需要与时俱进.这里是优秀爱好Java编程的集聚地,我们一起学习,共同进步.加入时请注明Java字样,谢谢.
Java编程技术交流QQ群171396965
作者:StellaAh 发表于2011-10-7 16:15:53 原文链接
阅读:393 评论:2 查看评论