黑马程序员技术交流社区

标题: 为什么这个程序线程安全了 [打印本页]

作者: heima_xyu    时间: 2014-5-7 14:29
标题: 为什么这个程序线程安全了
这是毕老师的生产者消费者代码:似乎是解决了线程安全问题

看下我推算的1-7步骤,有没有哪里有问题,如果没有,那么这个程序是否还是有安全问题?









package test;

public class ProducerCustomerDemo {
/**
  * @param args
  */
public static void main(String[] args) {
  // TODO Auto-generated method stub
  Resource res=new Resource();
  Producer pro1=new Producer(res);
  Producer pro2=new Producer(res);
  Customer cus1=new Customer(res);
  Customer cus2=new Customer(res);
  new Thread(pro1).start();
  new Thread(pro2).start();
  new Thread(cus1).start();
  new Thread(cus2).start();
}
}
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void  set(String name)
{
  while(flag)             //3.pro1进来,flag为true,进入wait状态,此时pro2也进来,也进入wait状态
  {                              
   try
   {
    wait();
   }
   catch(Exception e)
   {
   
   }
  }                                     //6.假设唤醒的pro1取的CPU使用权,打印了生产者商品2后,pro1还没执行到flag=true这段代码,就被pro2抢得使用权
  this.name=name+"--"+count++;              //1.假设pro1先进来,打印生产者商品1后
  System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);//7.pro2打印生产者商品3
  flag=true;             //2.pro1将flag变为true
  this.notifyAll();
}
public synchronized void  out(String name)
{
  while(!flag)                      //4.cus1进来,!flag为false,跳过该循环语句
  {
   try
   {
    wait();
   }
   catch(Exception e)
   {
   
   }
  }
  System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);  
  flag=false;
  this.notifyAll();              //5.打印消费者商品1,并更改flag为false,并唤醒其他所有线程
}
}
class Producer implements Runnable
{
  private Resource res;
  Producer(Resource res)
  {
   this.res=res;
  }
  public void run()
  {
   while(true)
   {
    res.set("+商品+");
   }
  }
}

class Customer implements Runnable
{
  private Resource res;
  Customer(Resource res)
  {
   this.res=res;
  }
  public void run()
  {
   while(true)
   {
    res.out("+商品+");
   }
  }
}




作者: ノtrack    时间: 2014-5-8 09:35
我都忘了...                  
作者: heima_xyu    时间: 2014-5-8 12:16
ノtrack 发表于 2014-5-8 09:35
我都忘了...

就是毕老师的消费者生产者例子:handshake
作者: sheng6699    时间: 2014-5-8 16:09
按照你的步骤,感觉有安全问题。两个线程都判断为while为false,将会继续执行下面的代码,生产两次,不知哪位能在看看是不是有安全问题啊
作者: sheng6699    时间: 2014-5-8 17:27
本帖最后由 sheng6699 于 2014-5-8 17:43 编辑

亲!  (你应该理解错了同步synchroizd锁), 当消费后falg=false时 ,如果两条线程执行wait,  那么两条线程就会争夺Synchroized锁,然后只有一条线程有资格执行while的条件。另一个线程没有资格执行了,不要理解错了哦:) 亲。
作者: Shimano    时间: 2014-5-8 18:28
class Resource
{
        private String name;
        private int count = 1;
        private boolean flag = false;
       
        public synchronized void set(String name)
        {
                while(flag)//1.t1跳过等待
                        try{this.wait();}catch(Exception e){}//4.t1等待,t2进来也等待        //10.t1首先重新持有锁,重新判断为false,不等待向下执行
                this.name = (name+"----"+count++);
                System.out.println(Thread.currentThread().getName()+"生产者..."+this.name);//2.生产1号商品,//11.t1生产2号商品
                flag = true;//3.t1更改标记        //12.t1改变标记,
                this.notifyAll();
                //(你以为在t1改变标记之前,flag仍为false,此时t2抢到执行权,然后生产商品3,覆盖t1生产的商品2,于是出现安全问题,解答如下:)
        }
       
        public synchronized void out()
        {
                while(!flag)//5.t3跳过等待        //9.t3和t4都等待
                        try{this.wait();}catch(Exception e){}
                System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);//6.正常消费1号商品,
                flag = false;//7.t3更改标记
                this.notifyAll();//8.t3把t1和t2都唤醒
        }
}
class Producer implements Runnable
{
        private Resource res;
        Producer(Resource res)
        {
                this.res = res;
        }
        public void run()
        {
                while(true)
                {
                        res.set("商品");
                }
        }
}
class Consumer implements Runnable
{
        private Resource res;
        Consumer(Resource res)
        {
                this.res = res;
        }
        public void run()
        {
                while(true)
                {
                        res.out();
                }
        }
}
class ProduceConsumeDemo_1
{
        public static void main(String[] args)
        {
                //new Thread(new Producer(res)).start();
                //new Thread(new Consumer(res)).start();
               
                Resource r = new Resource();
               
                Producer pro = new Producer(r);
                Consumer con = new Consumer(r);
               
                Thread t1 = new Thread(pro);//t1线程
                Thread t2 = new Thread(pro);//t2线程
                Thread t3 = new Thread(con);//t3线程
                Thread t4 = new Thread(con);//t4线程
               
                t1.start();
                t2.start();
                t3.start();
                t4.start();
        }
}
我的理解是:(我也是开始学习,只能查询来这些,要是你你弄清楚了,也麻烦给我简单回复下,谢谢......)
        虽然就代码看来,t1和t2都在同步中等待,
        一旦t1获取执行权,然后就立马重新持有该线程的锁,此时t2并不持有该线程的锁
        所以t2不能在t1没有执行完同步中代码的时候,就抢去t1的执行权。
       
        我猜想这可能是底层的一些机制控制的,或者说是更深入的东西。
       
        我查阅API看到wait()方法中有这样一句话,可能是这样理解的:
       
        该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,
        或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。
        然后该线程将等到重新获得对监视器的所有权后才能继续执行。
       
        还看到这句话,可以帮助我们理解:
       
        notifyAll方法唤醒等待队列中的所有的线程并把它们移入锁申请队列中
        当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
       
        等待中的多个线程,被唤醒后,只有一个线程有机会获得锁继续执行,
        其余的需要等待这一个线程释放锁之后才能继续执行。
       
        还有一点,需要注意:既然t1等待的时候,t2也能进来等待,说明t1等待时,
        已经释放锁了,否则t2也进不来啊!


作者: heima_xyu    时间: 2014-5-9 12:59
Shimano 发表于 2014-5-8 18:28
class Resource
{
        private String name;

哈哈,谢谢,差不多能理解了,回答的好详细{:3_67:}
作者: heima_xyu    时间: 2014-5-9 13:00
sheng6699 发表于 2014-5-8 17:27
亲!  (你应该理解错了同步synchroizd锁), 当消费后falg=false时 ,如果两条线程执行wait,  那么两条线 ...

嗯,是的,回头看了下毕老师讲解if(flag)不同步,都是将某个线程执行完,另外一个才能拿到锁,继续执行。
确实只有一条线程拿到锁,然后执行完
作者: sheng6699    时间: 2014-5-9 19:06
heima_xyu 发表于 2014-5-9 13:00
嗯,是的,回头看了下毕老师讲解if(flag)不同步,都是将某个线程执行完,另外一个才能拿到锁,继续执行 ...

呵呵。。。。。。。。
作者: 倪大大    时间: 2014-5-9 21:38
我当初卡过一个小问题,现在分享给新手,很基础的东西
sleep() 线程睡着 但不释放锁
wait(0 线程等待  会释放持有的锁




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2