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

发布于 2022-09-12 13:54:22 字数 2976 浏览 16 评论 0

学习多线程时写的模拟抢票程序,使用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张票

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜`诱少女 2022-09-19 13:54:22

这是一个线程同步的问题,它解决的是多线程的安全问题。
多线程谁先运行,谁能抢到更多的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()了。

英雄似剑 2022-09-19 13:54:22

synchronized 是非公平锁,不做公平保证。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文