读书人

Java线程同步经典有关问题-卖票有关问

发布时间: 2012-04-21 14:34:44 作者: rapoo

Java线程同步经典问题-卖票问题有个地方不懂??
各位大虾,问题有点长,又有点绕,但是我相信看完后都能理解我的困惑在哪,希望能有前辈耐心看完并指导下后辈,万分感谢!

Java code
class A implements Runnable{    public static int tickets = 100;            public void run()    {        while (true)        {            if (tickets > 0)            {                System.out.printf("%s线程正在卖出第%d张票\n",                    Thread.currentThread().getName(), tickets);                --tickets;                }            else            {                break;            }        }    }}public class TestTickets{    public static void main(String[] args)    {        A aa1 = new A();        Thread t1 = new Thread(aa1);        t1.start();                    A aa2 = new A();        Thread t2 = new Thread(aa2);        t2.start();            }}

程序运行后有种这样的情况:
Thread-0线程正在卖出第100张票
Thread-0线程正在卖出第99张票 //问题一
Thread-1线程正在卖出第99张票
Thread-1线程正在卖出第97张票 //问题二
Thread-1线程正在卖出第96张票
Thread-1线程正在卖出第95张票
Thread-0线程正在卖出第98张票 //问题三
Thread-0线程正在卖出第94张票
Thread-0线程正在卖出第93张票
Thread-0线程正在卖出第92张票
...
在这里呢有三个问题,第一:第99张票被卖了两次;第二:Thread-1卖玩第99张票后不是卖第98张票而是第97张票;第三:Thread-0卖出第95张票后Thread-1又卖第98张票.
问题一和问题二我都能理解,但是问题三却怎么也理解不了,因为虽然是不同线程,但我的Tickets是静态的,是被共享的,因为这个才出现了问题二,对于问题二我的理解是:Thread-0卖出第99张票还没减1就转到Thread-1,于是Thread-1也卖出第99张票,并且减一后又转到Thread-0,Tickets变成了98,然后Thread-0继续未完成的--Tickets步骤,Tickets就变成了97,于是又转到了Thread-1,就造成了Thread-1是卖出第97张,而不是第98张.
但是对于问题三我却不能理解,虽然在问题二中Thread-0线程Tickets变成98后就没有执行,如果继续应该是98,但是我前面也说了Tickets是静态的,那么在Thread-1卖出第95张后转到Thread-0,Tickets要么是95,要么是94,也不应该是98啊???不懂???

[解决办法]
因为PrintStream的format方法里面是同步的,所以才发生这种情况!
[解决办法]
tickets 是两个线程的共享数据,对于共享的数据必须要加同步操作。比方说thread0卖出99张还没--操作,thread1同时输出,那么还是99张,然后thread0再--tickets
[解决办法]
LZ..没用到多线程啊
[解决办法]
你没有synchronized线程
[解决办法]
程序运行后有种这样的情况:
Thread-0线程正在卖出第100张票
Thread-0线程正在卖出第99张票 //问题一
Thread-1线程正在卖出第99张票
Thread-1线程正在卖出第97张票 //问题二
Thread-1线程正在卖出第96张票
Thread-1线程正在卖出第95张票
Thread-0线程正在卖出第98张票 //问题三
Thread-0线程正在卖出第94张票
Thread-0线程正在卖出第93张票
Thread-0线程正在卖出第92张票


lz的问题是在问题三哪里么?
为什么会先打97 96 会出现在98 前面?
还有为什么会重复出现吧?
lz可以这样理解 当程序运行到
System.out.printf("%s线程正在卖出第%d张票\n",
Thread.currentThread().getName(), tickets);
这里的时候 tickets 是 99 但是并没有打印出来。(没有运行下面的--tickets)
这个时候线程2也进来了 这个时候tickets的值还是99 所有会出现重复
问题2 跟问题3 同理~~
加入Synchronized 可以解决问题
[解决办法]
要明白输出的结果,关键看线程的阻塞(被挂起)点, 再次获得运行权时还要接着从这执行.

根据楼主的输出, 问题三是这样出现的:
线程0输出第一条结果"Thread-0线程正在卖出第100张票"后,还有执行权, 执行了下一条语句,--tickets,
tickets=99.
从第二条输出看,线程0第一次循环结束,并且仍有执行权,进入第二轮循环,输出“Thread-0线程正在卖出第99张票 ”。
从第三条输出看,在线程0第二次循环输出第二条结果后(在执行--tickets之前),线程0被挂起了,线程1获得执行权,输出“Thread-1线程正在卖出第99张票”。

这块就是最关键的地方,接下来的线程1是继续执行还是挂起了呢?
从后面的输出看,特别是楼主的第三点疑惑,说明此时,线程1被挂起(在执行--tickets之前)。线程0又获得了执行权,执行了 --tickets语句,此时tickets=98. 线程0进入下一轮循环,在执行
System.out.printf("%s线程正在卖出第%d张票\n",
Thread.currentThread().getName(), tickets);
这条语句还没有完(tickets已经传入98,但还没显示出来),而线程0就又被挂起了。线程1获得执行权,它接着--tickets 执行,tickets=97, 并进入下一轮循环,线程1执行了3轮循环,都没被挂起,连续输出了:

Thread-1线程正在卖出第97张票 //问题二
Thread-1线程正在卖出第96张票
Thread-1线程正在卖出第95张票

这几条结果。在语句 --tickets,之前(此时tickets=95),线程1被挂起,线程0获得执行权, 接着刚才挂起的地方继续执行,把那个没显示完的 98 显示出来,并接着执行--tickets,(tickets=94) 一直到楼主的给出的输出,线程0一直有执行权,输出了后面的结果。



以上个人的看法,表达的未必清楚。


[解决办法]
在你结贴前,我补充下我的看法。
首先 System.out.printf();这一句并不是原子性操作,即在输出的过程中可能切换到另外的线程中去。
所以你的问题三的解释是在执行输出98这条语句时,
正好切换到线程一,并先执行了
Thread-1线程正在卖出第99张票
Thread-1线程正在卖出第97张票
Thread-1线程正在卖出第96张票
Thread-1线程正在卖出第95张票

等再次切换到线程0的时候再把98这条输了出来,楼上8楼,估计想表达的也是这个意思;
其次,为了保证你对ticket值的改动能及时写到内存里,而不让编译器做任何优化,你应该在声明时加volatile关键字。

读书人网 >J2SE开发

热点推荐