A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 贾存双 中级黑马   /  2012-7-12 23:41  /  2253 人查看  /  6 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

class MyThread implements Runnable{
private int ticket = 5 ;
public void run(){
  this.sale() ;
}
public synchronized void sale(){
  for(int i=0;i<100;i++){
   if(ticket>0){
    try{
     Thread.sleep(2000) ;
    }catch(InterruptedException e){
     e.printStackTrace() ;
    }
    System.out.println(Thread.currentThread().getName() + "卖票ticket = " + ticket--) ;
   }
  }
}
} ;
public class SaleTicket02{
public static void main(String args[]){
  MyThread thr = new MyThread() ;
  Thread a = new Thread(thr,"线程A") ;
  Thread b = new Thread(thr,"线程B") ;
  Thread c = new Thread(thr,"线程C") ;
  a.start();
  b.start();
  c.start();
}
} ;
结果:
线程A卖票ticket = 5
线程A卖票ticket = 4
线程A卖票ticket = 3
线程A卖票ticket = 2
线程A卖票ticket = 1
为什么只有线程A卖票啊???

6 个回复

倒序浏览
本帖最后由 张莹莹 于 2012-7-13 00:01 编辑

关于同步问题的解释,详情看图中注释~
出现此问题的关键在于for循环的位置不正确,
首先a线程启动,会执行sale()方法,这个方法是synchronized所以是互斥访问的,
当线程A占有此方法之后,会阻止线程B和C使用此方法,直到线程A执行完毕此for循环,
此时,A必然在5次循环之内购买5张票,但是B和C在A的for循环执行完毕前,完全没有机会去执行此for循环
正确的方法应该是将for循环写到run()方法中。

错误解释.png (114.83 KB, 下载次数: 23)

问题解释

问题解释

正确方法.png (113.31 KB, 下载次数: 28)

修改后的代码

修改后的代码

正确方法运行结果.png (89.58 KB, 下载次数: 27)

更改for循环位置之后的运行结果

更改for循环位置之后的运行结果

评分

参与人数 1技术分 +1 收起 理由
韦念欣 + 1 赞一个!

查看全部评分

回复 使用道具 举报
当i=5时ticket =0,以后就不会在打印东西,知道2*95秒后,for循环执行完毕b线程才开始执行,不是只有a执行,只要lz等待5分钟便可看到b或c买的东西
回复 使用道具 举报
当i=5时ticket =0,以后就不会在打印东西,知道2*95秒后,for循环执行完毕b线程才开始执行,不是只有a执行,只要lz等待5分钟便可看到b或c买的东西
回复 使用道具 举报
你的卖票程序就5张票,你要知道cpu都是好几个G赫兹的,一秒的运算速率是难以想象的,线程A启动后执行同步方法slae(),瞬间卖光了所有的票。因为sale()是同步方法,即使你在代码块中让获得资源的线程休眠,别的线程也不能进入sale()方法,2楼同学的方法就是不错的选择,把sale()方法写到了循环当中,并通过this关键字引用当前线程执行sale()方法。
回复 使用道具 举报
用同步代码块也是可以的

class MyThread implements Runnable
{
Object obj=new Object();//创建Object对象只是为了得到一把锁,其实也可以用别的,只要唯一就行
                         //例如:SaleTicket02.class,MyThread.class。。
private int ticket = 100;//ticket的值最好大一点,太小了看不出结果
public void run()
{
  this.sale() ;
}
public  void sale( )
{
     
  for(int i=0;i<100;i++)
  {
    synchronized(obj)//同步代码块
{
     if(ticket>0){
     try{
         Thread.sleep(20 ) ;
        }
   catch(InterruptedException e)
         {
              e.printStackTrace() ;
             }
      System.out.println(Thread.currentThread().getName() + "卖票ticket = " + ticket--);
   
   }
   }
  }
}
}  
public class SaleTicket02
{
public static void main(String args[])
{
  MyThread thr = new MyThread() ;
  Thread a = new Thread(thr,"线程A") ;
  Thread b = new Thread(thr,"线程B") ;
  Thread c = new Thread(thr,"线程C") ;
  a.start();
  b.start();
  c.start();
   
}
}  

回复 使用道具 举报
柯玲 中级黑马 2012-7-13 09:43:24
7#
首先要明确同步关键字应用的范围:
1.哪些代码是多线程运行代码
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的
从程序中知道:
1.
  1. if(ticket>0){
  2. try{
  3. Thread.sleep(2000) ;
  4. }catch(InterruptedException e){
  5. e.printStackTrace() ;
  6. }
  7. System.out.println(Thread.currentThread().getName() + "卖票ticket = " + ticket--) ;
  8. }
复制代码
是多线程运行代码

2.ticket=5是共享数据
3.if(ticket>0)和打印语句中的ticket--是操作共享数据的
因此我们的同步关键字应该将if语句范围内的东西包围起来。
而此程序中你所要同步代码的范围有误,造成一个线程进入run方法内的for语句之后就把此段代码上锁,直接执行完。

如果按照此程序的代码,分析上述结果出现的原因:
假如线程A拿到CPU执行权进入run()方法内,它就按for循环执行100次,而ticket=5,在前五次就可以把票卖完,后面的95次都不符合ticket>0的条件,遂不执行if语句的内容。导致结果票全由线程A卖出。在100次运行没有结束前,其它线程都处于冻结状态中。当for循环执行完毕,假若线程B拿到CPU执行权,在进入同步锁以后,也执行100次,依次类推,虽然票已卖完,但每个程序都会进入同步代码块中,执行100次。

评分

参与人数 1技术分 +1 收起 理由
黑马张扬 + 1

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马