Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.2k views
in Technique[技术] by (71.8m points)

关于java中synchronized同一个线程获取锁的频率问题

学习多线程时写的模拟抢票程序,使用jdk1.7 win64
使用synchronized之后同一个线程连续抢票的概率很高,这是为什么?

public class SellTicket implements Runnable {

    public int ticket = 20;
    public int count = 0;

    @Override
    public void run() {

        while (true) {

            if (ticket > 0) {
                ticket--;
                count++;
                System.out.println(Thread.currentThread().getName() + "抢到了第" + count + "张票,剩余" + ticket + "张票");
            } else {
                break;
            }

        }
    }

    public static void main(String[] args) {
        SellTicket st = new SellTicket();
        Thread t1 = new Thread(st, "张三");
        Thread t2 = new Thread(st, "李四");
        Thread t3 = new Thread(st, "王五");

        t1.start();
        t2.start();
        t3.start();

    }

}

输出的顺序是这样的,各个线程交替抢票

张三抢到了第1张票,剩余19张票
李四抢到了第2张票,剩余18张票
王五抢到了第4张票,剩余16张票
张三抢到了第3张票,剩余17张票
张三抢到了第7张票,剩余13张票
王五抢到了第6张票,剩余14张票
王五抢到了第9张票,剩余11张票
王五抢到了第10张票,剩余10张票
李四抢到了第5张票,剩余15张票
王五抢到了第11张票,剩余9张票
王五抢到了第13张票,剩余7张票
张三抢到了第8张票,剩余12张票
张三抢到了第15张票,剩余5张票
张三抢到了第16张票,剩余4张票
张三抢到了第17张票,剩余3张票
张三抢到了第18张票,剩余2张票
王五抢到了第14张票,剩余6张票
王五抢到了第20张票,剩余0张票
李四抢到了第12张票,剩余8张票
张三抢到了第19张票,剩余1张票

加上synchronized之后

@Override
public void run() {

    while (true) {
        synchronized (this) {
            if (ticket > 0) {
                ticket--;
                count++;
                System.out.println(Thread.currentThread().getName() + "抢到了第" + count + "张票,剩余" + ticket + "张票");
            } else {
                break;
            }
        }

    }
}

输出变成了这样,同一个线程抢票的概率很高,经常只有一个线程在抢票

张三抢到了第1张票,剩余19张票
王五抢到了第2张票,剩余18张票
王五抢到了第3张票,剩余17张票
王五抢到了第4张票,剩余16张票
王五抢到了第5张票,剩余15张票
王五抢到了第6张票,剩余14张票
王五抢到了第7张票,剩余13张票
王五抢到了第8张票,剩余12张票
王五抢到了第9张票,剩余11张票
王五抢到了第10张票,剩余10张票
王五抢到了第11张票,剩余9张票
王五抢到了第12张票,剩余8张票
王五抢到了第13张票,剩余7张票
王五抢到了第14张票,剩余6张票
王五抢到了第15张票,剩余5张票
王五抢到了第16张票,剩余4张票
王五抢到了第17张票,剩余3张票
王五抢到了第18张票,剩余2张票
王五抢到了第19张票,剩余1张票
王五抢到了第20张票,剩余0张票

如果在synchronized之前sleep一下,又变成交替抢票了

@Override
public void run() {

    while (true) {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        synchronized (this) {
            if (ticket > 0) {
                ticket--;
                count++;
                System.out.println(Thread.currentThread().getName() + "抢到了第" + count + "张票,剩余" + ticket + "张票");
            } else {
                break;
            }
        }

    }
}
李四抢到了第1张票,剩余19张票
张三抢到了第2张票,剩余18张票
王五抢到了第3张票,剩余17张票
王五抢到了第4张票,剩余16张票
张三抢到了第5张票,剩余15张票
李四抢到了第6张票,剩余14张票
王五抢到了第7张票,剩余13张票
张三抢到了第8张票,剩余12张票
李四抢到了第9张票,剩余11张票
李四抢到了第10张票,剩余10张票
张三抢到了第11张票,剩余9张票
王五抢到了第12张票,剩余8张票
王五抢到了第13张票,剩余7张票
张三抢到了第14张票,剩余6张票
李四抢到了第15张票,剩余5张票
王五抢到了第16张票,剩余4张票
张三抢到了第17张票,剩余3张票
李四抢到了第18张票,剩余2张票
李四抢到了第19张票,剩余1张票
王五抢到了第20张票,剩余0张票

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

这是一个线程同步的问题,它解决的是多线程的安全问题。
多线程谁先运行,谁能抢到更多的cpu权,在没有设置优先级的情况的下,通常是系统决定,随机的,我们掌控不了。
每个线程运行时,都会先拿到时间片,然后才会执行。

来看这个,抢票程序,总共有20张票,三个抢票线程共享,
首先看下没有加synchronized这个情况下,线程是不安全的,多执行几次,可以看到如下(直接复制的楼主的代码运行,没做任何改变):

李四抢到了第2张票,剩余18张票 // 第2张票
王五抢到了第3张票,剩余17张票
张三抢到了第2张票,剩余18张票 // 第2张票
王五抢到了第5张票,剩余15张票
......

而且也不是交替抢票,有的多,有的少,就楼主粘贴的运行结果也是一样,并不是交替,也是完全随机。
然后使用了同步代码块synchronized,获取同步锁,则保证线程安全了。

至于为什么会有一个线程抢占资源高,或者经常一个线程抢占资源,那不是代码问题,因为这个结果完全随机,系统决定,

举个例子,有20个苹果,放苹果的地有一个红旗,A、B、C 3个人来抢夺苹果,抢夺规则,只有先拿到红旗的人会有5秒抢夺苹果的时间,未拿到旗子的人不能抢夺,5秒之后,拿旗子的人停止抢夺苹果并将旗子放回原处,之后又可以重新抢旗子,获得抢苹果的权利,比如A拿到了旗子,5秒内,他速度非常快,5秒就把20个苹果抢完了,那其他人就没得抢了。

这个旗子就相当于synchronized获得的同步锁,5秒就等同于线程执行时获得的时间片,20个苹果等同于那20张票。

那么规则更改下,拿到旗子时,必须挥动旗子4秒,然后再抢苹果,那这5秒抢夺时间减去4秒就剩下1秒时间,那抢苹果的时间少了,自然其他人能够拿到旗子获得抢苹果时间的机会也多了,这个挥动旗子,就类似sleep()了。


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...