日期:2014-05-20  浏览次数:20799 次

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一直有执行权,输出了后面的结果。