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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© bullfrog 中级黑马   /  2014-9-1 15:47  /  1341 人查看  /  6 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 bullfrog 于 2014-9-2 11:02 编辑
  1. class  Demo4
  2. {
  3.                 public static void main(String[] args)throws Exception{
  4.                         Demo d = new Demo();
  5.                         Thread t1 = new Thread(d);
  6.                         Thread t2 = new Thread(d);
  7.                         t1.start();
  8.                         t2.start();
  9.                         Thread.sleep(600);
  10.                         System.out.println("......"+ Demo.flag+"......."+d.num);
  11.                 }
  12. }

  13. class Demo implements Runnable
  14. {        
  15.         int num = 0;
  16.         static int flag = 0;
  17.         public void run(){
  18.                 sale();
  19.         }
  20.         
  21.         public void sale(){
  22.                 while(this.num<20){
  23.                 flag ++;
  24.                 System.out.println(Thread.currentThread().getName()+":::::"+this.num++);
  25.                 }
  26.         }        
  27. }
复制代码
这样运行之后,有时会出现“0在两个线程都出现的情况”;我知道这是多线程的安全性问题。
但是另一个问题是:
当最后,主线程等待一段时间后,main函数打印出Demo.flag和d.num时,结果有点奇怪。会出现下面两种结果:
......21.......20    或者:
......20.......20

我的想法是:如果flag++执行了一次,那么this.num++ 也同样会执行一次。无论两个线程怎么交替,当两个线程都执行完毕后,总执行次数应该是相等的吧。
既然相等,那为什么会有这种结果呢?



6 个回复

倒序浏览
问题是你没法保证cpu不会在flag++和num--之间切换到main线程去执行System.out.println("......"+ Demo.flag+"......."+d.num);语句不会插

点评

主线程sleep()的时间足够让那两个线程执行完毕了,但是仍然是这个结果。所以应该不是这样。  发表于 2014-9-1 16:26

评分

参与人数 1技术分 +1 收起 理由
舍我其谁 + 1

查看全部评分

回复 使用道具 举报
明白了,还是线程安全问题。当num=19,flag=19时,t1进入while循环,flag自增1变为20,此时轮到t2执行,由于num还是20,t2也进入while,flag自增1变成21
回复 使用道具 举报
本帖最后由 bullfrog 于 2014-9-1 17:02 编辑
fantacyleo 发表于 2014-9-1 16:44
明白了,还是线程安全问题。当num=19,flag=19时,t1进入while循环,flag自增1变为20,此时轮到t2执行,由 ...

对啊,但是情况如下:
num =19; flag=19;
t1进入while循环,flag自增1,此时num没有自增,CPU切换到t2,同样满足条件进入while循环;
接下来,t2使flag自增1;这个时候 flag = 21;

但是; t1, t2还没执行完啊,下面的 num++在两条线都是一定会执行的,不是吗?也就是两次num++;
那么最终 num 应该是21啊?可是我试验那么多次,没有出现这种情况。

其实说到底就是,flag++这行语句与num++这行语句执行的次数应该是一样的,而它们的初始值又相同,怎么会最后结果不一样呢。
回复 使用道具 举报
本帖最后由 fantacyleo 于 2014-9-1 18:20 编辑
bullfrog 发表于 2014-9-1 17:01
对啊,但是情况如下:
num =19; flag=19;
t1进入while循环,flag自增1,此时num没有自增,CPU切换到t2,同样 ...

多线程问题是不能简单用运行了XXX次来判断一个命题是否成立的,运行1万次正确也不能保证下一次正确。必须从理论上排除出错的各种可能才能证明它是正确的。这里的一种可能是内存可见性。就是说即便t1先于t2执行num++,但不能保证t2执行num++时的num一定是被t1增加过的,因为t1执行的num++结果可能还没从cpu写入内存就切换到t2执行了,t2看到的num还是未被t1增加过的值。这也就是使用volatile关键字的意义和使用同步的意义之一:确保内存一致性

评分

参与人数 1技术分 +1 收起 理由
舍我其谁 + 1

查看全部评分

回复 使用道具 举报
本帖最后由 bullfrog 于 2014-9-1 20:39 编辑
fantacyleo 发表于 2014-9-1 18:07
多线程问题是不能简单用运行了XXX次来判断一个命题是否成立的,运行1万次正确也不能保证下一次正确。必须 ...

原来如此,解开了疑惑。搜索了volatile,大概搞清楚了一些。

也就是比如当num=5时,即使t1, t2都执行了 num++, 但是在内存中写入的可能是两个 5+1的动作,是这样吧。
改一下: “当num=5"的说法不对,因为不加volatile的话,线程检查的是自己的副本num,没有统一的值。


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