2 sinat 32123569 sinat_32123569 于 2017.01.13 12:36 提问

java多线程的一个简单问题 20C
 public class Demo6 {
    public static void main(String[] args) {
        Thread6 r = new Thread6();
        Thread t1 = new Thread(r); 
        Thread t2 = new Thread(r); 
        t1.start();
        t2.start();
    }
}

class Thread6 implements Runnable {
    int ticket = 100;
    @Override
    public void run() {
        while(true) {
            if(ticket>0) {
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"卖。。。"+ticket--);               
            } else {
                break;
            }

        }
    }

}

为什么加了中间的那一段sleep,就出现一定数量的重复卖票,而不加就基本不会出现?输出的时候读到的不应该都是最新的ticket值吗? 如果在输出语句下加一段sleep紧接着ticket再减1,可以理解会大量重复,但是sleep放在上面为什么也会重复呢?

6个回答

z_asdf
z_asdf   2017.01.13 14:07
   Java中原子性的操作,是指读和写是原子性的,比如i=5;这个就是一个原子性的操作。多线程执行时,只能在一个原子操作里,才会没有并发读或并发写的情况。通常情况下,在Java里面,i++或者i--不是线程安全的,这里面有三个独立的操作:获得变量当前值,为该值+1/-1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作是“原子性”的。这就是为什么会出现一定数量的重复卖票。
        另外,sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。所以,你加上sleep()以后,就增加了并发操作ticket的机会,因此出现重复卖票的可能就会增加。还有你说不加就不出现只是人品问题,这种情况的并发修改本来就是随机的,有可能发生,有可能不发生。
sinat_32123569
sinat_32123569 xiexie回答
11 个月之前 回复
welan123123
welan123123 大牛啊
11 个月之前 回复
u011606457
u011606457   2017.01.13 13:07

sleep放上、放下不是一样的吗?要转变思维方式,特别是多线程情况下

sinat_32123569
sinat_32123569 不一样的吧?放在上面sleep并不会影响下面读ticket的值啊?
11 个月之前 回复
YXTS122
YXTS122   2017.01.13 13:21

同问,帮你顶上来。。。。。。。。。

welan123123
welan123123   2017.01.13 13:58

把你的问题说清楚啊,你指的重复买票是什么怎样的重复?你改一下你的代码,把ticket值也输出看看

qq_33750826
qq_33750826   2017.01.13 17:12

你好,很高兴为你解答,首先你有一个Runnable,两个线程共用一个Runnable,说明100张票在两个线程是公用的,就算你使用了sleep方法,也不可能出现重复的数据,最多出现数字数字顺序的打印

sinat_32123569
sinat_32123569 xiexie回答
11 个月之前 回复
qq_33750826
qq_33750826 打错了,不是顺序的印,是数据无序的打印,因为多线程是存在安全问题的
11 个月之前 回复
sycdzdd
sycdzdd   2017.01.14 12:59

代码对应的thread的构造函数 public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
},最终调用的是 public void run() {
if (target != null) {
target.run();
}
},也就是两个线程是直接调用的target对象的代码,那么也就是共享ticket这个变量,而ticket的自加和自减不是原子性操作,并且对于多线程来讲也有可见性的问题,即线程缓存的问题,如果只是自增自减可以使用atomic包,但如果外围同样有针对此变量的比较如ticket>0的话,可能就需要加锁来同步了

sinat_32123569
sinat_32123569 xiexie回答
11 个月之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!