多线程(并发)买票的案例详解同步代码块和同步方法 (复习)


前言

复习多线程章节的时候有些疑问,想想后,理解的更深了。由此来记录以下。希望也可以帮助其他的人。


简单的原理: 队列+锁


常见的买票案例(线程安全:多个线程操作同一个数据。)

public class TestTicket02 implements Runnable{

    //库存中有 100 张票
    private int ticket = 100;

    //在这个方法中,进行买票
    @Override
    public void run() {

        //怎么买票? 票没就结束
        //循环买票
        while(true){
            
            // 票没有了,就进行停止
            if(ticket <= 0){
                break;
            }

            //有票就进行卖票
            System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
            //买了一张,就少一张票
            ticket--;
            
        }

    }

    public static void main(String[] args) {

        TestTicket02 testTicket02 = new TestTicket02();
        new Thread(testTicket02,"1").start();
        new Thread(testTicket02,"2").start();
        new Thread(testTicket02,"3").start();

    }
}


结果:

1买了第1003买了第1003买了第982买了第1003买了第971买了第993买了第952买了第963买了第931买了第943买了第912买了第923买了第891买了第903买了第872买了第883买了第851买了第863买了第832买了第843买了第811买了第823买了第792买了第803买了第771买了第783买了第752买了第763买了第731买了第743买了第712买了第723买了第691买了第703买了第672买了第683买了第651买了第661买了第621买了第611买了第601买了第591买了第581买了第571买了第561买了第551买了第541买了第531买了第521买了第511买了第501买了第491买了第481买了第471买了第461买了第451买了第441买了第431买了第421买了第411买了第401买了第391买了第381买了第371买了第361买了第351买了第341买了第331买了第321买了第311买了第301买了第291买了第281买了第271买了第261买了第251买了第241买了第231买了第221买了第211买了第201买了第191买了第181买了第173买了第632买了第643买了第151买了第161买了第121买了第111买了第101买了第93买了第132买了第143买了第71买了第83买了第52买了第63买了第31买了第43买了第12买了第2


这里添加Thread.sleap(…) 有不同的结果。
会导致:
1
0
-1
的结果


使用同步代码块或者同步方法(解决线程不安全的问题)

添加的地方不同,会有不同的结果。


添加的方式一:synchronized 关键字

由于只进行修改run()中的代码所有,只显示这部分代码


@Override
    public synchronized void run() {		
        //怎么买票? 票没就结束
        //循环买票
        while(true){

            // 票没有了,就进行停止
            if(ticket <= 0){
                break;
            }

            //有票就进行卖票
            System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
            //买了一张,就少一张票
            ticket--;
            }
    }


结果:

1买了第100票
1买了第99票
1买了第98票
1买了第97票
1买了第96票
1买了第95票
1买了第94票
1买了第93票
1买了第92票
1买了第91票
1买了第90票
1买了第89票
1买了第88票
1买了第87票
1买了第86票
1买了第85票
1买了第84票
1买了第83票
1买了第82票
1买了第81票
1买了第80票
1买了第79票
1买了第78票
1买了第77票
1买了第76票
1买了第75票
1买了第74票
1买了第73票
1买了第72票
1买了第71票
1买了第70票
1买了第69票
1买了第68票
1买了第67票
1买了第66票
1买了第65票
1买了第64票
1买了第63票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
1买了第36票
1买了第35票
1买了第34票
1买了第33票
1买了第32票
1买了第31票
1买了第30票
1买了第29票
1买了第28票
1买了第27票
1买了第26票
1买了第25票
1买了第24票
1买了第23票
1买了第22票
1买了第21票
1买了第20票
1买了第19票
1买了第18票
1买了第17票
1买了第16票
1买了第15票
1买了第14票
1买了第13票
1买了第12票
1买了第11票
1买了第10票
1买了第9票
1买了第8票
1买了第7票
1买了第6票
1买了第5票
1买了第4票
1买了第3票
1买了第2票
1买了第1票


分析结果:

线程名为1 的线程,执行了全部的票 。

  1. 为什么是线程名为1 的线程 执行? 原因:根据 cpu 自动调度的。
  2. 为什么线程1 执行了全部?
    原因:synchronized 标注在了run()方法上了。
    代表了,3个线程,进行排队(形成队列)停留在run()方法的前面,一个一个的等待执行。cpu调度,把锁(this)随机分配给任意的一个线程。(由于线程1先new ,所以先进行调度)。run()执行完了后,ticket等于0了,其他的线程拿到了锁,进入了run()中,由于ticket等于0,所以相当于没有执行。


添加方式二:代码块(注意位置)

//在这个方法中,进行买票
    @Override
    public  void run() {

        synchronized(this){		
            //怎么买票? 票没就结束
            //循环买票
            while(true){

                // 票没有了,就进行停止
                if(ticket <= 0){
                    break;
                }

                //有票就进行卖票
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
                //买了一张,就少一张票
                ticket--;
            }
        }
       
       
    }


结果:

1买了第100票
1买了第99票
1买了第98票
1买了第97票
1买了第96票
1买了第95票
1买了第94票
1买了第93票
1买了第92票
1买了第91票
1买了第90票
1买了第89票
1买了第88票
1买了第87票
1买了第86票
1买了第85票
1买了第84票
1买了第83票
1买了第82票
1买了第81票
1买了第80票
1买了第79票
1买了第78票
1买了第77票
1买了第76票
1买了第75票
1买了第74票
1买了第73票
1买了第72票
1买了第71票
1买了第70票
1买了第69票
1买了第68票
1买了第67票
1买了第66票
1买了第65票
1买了第64票
1买了第63票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
1买了第36票
1买了第35票
1买了第34票
1买了第33票
1买了第32票
1买了第31票
1买了第30票
1买了第29票
1买了第28票
1买了第27票
1买了第26票
1买了第25票
1买了第24票
1买了第23票
1买了第22票
1买了第21票
1买了第20票
1买了第19票
1买了第18票
1买了第17票
1买了第16票
1买了第15票
1买了第14票
1买了第13票
1买了第12票
1买了第11票
1买了第10票
1买了第9票
1买了第8票
1买了第7票
1买了第6票
1买了第5票
1买了第4票
1买了第3票
1买了第2票
1买了第1票


结果分析:

		1. 三个线程同时进入了run()方法中。
		在这个方法的前面进行停下来了。进行排队(队列),一个一个的执行该方法。
		当一个线程执行完后,ticket==0了,23线程拿到了this对象,不会执行什么了。
		和前面的结果一样。
		synchronized(this){		
            //怎么买票? 票没就结束
            //循环买票
            while(true){

                // 票没有了,就进行停止
                if(ticket <= 0){
                    break;
                }

                //有票就进行卖票
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
                //买了一张,就少一张票
                ticket--;
            }
        }

添加方式三:代码块(注意位置)

//在这个方法中,进行买票
    @Override
    public  void run() {

        //怎么买票? 票没就结束
        //循环买票
        while(true){

            synchronized (this){
                // 票没有了,就进行停止
                if(ticket <= 0){
                    break;
                }

                //有票就进行卖票
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
                //买了一张,就少一张票
                ticket--;
            }

        }

    }

结果:

1买了第100票
1买了第99票
1买了第98票
1买了第97票
1买了第96票
1买了第95票
1买了第94票
1买了第93票
1买了第92票
1买了第91票
1买了第90票
1买了第89票
1买了第88票
1买了第87票
1买了第86票
1买了第85票
1买了第84票
1买了第83票
1买了第82票
1买了第81票
1买了第80票
1买了第79票
1买了第78票
1买了第77票
1买了第76票
1买了第75票
1买了第74票
1买了第73票
1买了第72票
1买了第71票
1买了第70票
1买了第69票
1买了第68票
1买了第67票
1买了第66票
1买了第65票
1买了第64票
1买了第63票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
3买了第36票
3买了第35票
3买了第34票
3买了第33票
3买了第32票
3买了第31票
3买了第30票
3买了第29票
3买了第28票
3买了第27票
3买了第26票
3买了第25票
3买了第24票
3买了第23票
3买了第22票
3买了第21票
3买了第20票
3买了第19票
3买了第18票
3买了第17票
3买了第16票
3买了第15票
3买了第14票
3买了第13票
3买了第12票
3买了第11票
3买了第10票
3买了第9票
3买了第8票
3买了第7票
3买了第6票
3买了第5票
3买了第4票
3买了第3票
3买了第2票
3买了第1票

结果分析:

		while(true){
			
			1. 代表了3个线程都进入了 while()方法,
			在下面的这个方法进行排队(队列),一个一个的拿锁,进行执行。
			这种情况有时,会跟情况1的结果相同,但是执行的过程是不同的,下面的是由于cpu执行的过快,导致的。
			可以添加Thread.sleep()方法,便可以看见不同的结果。()如下
            synchronized (this){
                // 票没有了,就进行停止
                if(ticket <= 0){
                    break;
                }

                //有票就进行卖票
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
                //买了一张,就少一张票
                ticket--;
            }

        }
@Override
    public  void run() {

        //怎么买票? 票没就结束
        //循环买票
        while(true){

            synchronized (this){
                // 票没有了,就进行停止
                if(ticket <= 0){
                    break;
                }

                //有票就进行卖票
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
                //买了一张,就少一张票
                ticket--;
            }

            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }

    }

结果:

1买了第100票
3买了第99票
2买了第98票
1买了第97票
3买了第96票
2买了第95票
3买了第94票
2买了第93票
1买了第92票
1买了第91票
2买了第90票
3买了第89票
1买了第88票
2买了第87票
3买了第86票
1买了第85票
2买了第84票
3买了第83票
1买了第82票
2买了第81票
3买了第80票
1买了第79票
2买了第78票
3买了第77票
3买了第76票
1买了第75票
2买了第74票
3买了第73票
2买了第72票
1买了第71票
2买了第70票
1买了第69票
3买了第68票
2买了第67票
1买了第66票
1买了第65票
2买了第64票
3买了第63票
2买了第62票
1买了第61票
3买了第60票
1买了第59票
2买了第58票
3买了第57票
1买了第56票
2买了第55票
3买了第54票
2买了第53票
1买了第52票
3买了第51票
2买了第50票
1买了第49票
3买了第48票
1买了第47票
2买了第46票
3买了第45票
1买了第44票
2买了第43票
1买了第42票
2买了第41票
3买了第40票
2买了第39票
3买了第38票
1买了第37票
3买了第36票
1买了第35票
2买了第34票
2买了第33票
3买了第32票
1买了第31票
3买了第30票
2买了第29票
1买了第28票
2买了第27票
3买了第26票
3买了第25票
1买了第24票
2买了第23票
1买了第22票
2买了第21票
3买了第20票
3买了第19票
2买了第18票
1买了第17票
2买了第16票
3买了第15票
1买了第14票
3买了第13票
1买了第12票
2买了第11票
1买了第10票
3买了第9票
2买了第8票
3买了第7票
1买了第6票
2买了第5票
1买了第4票
3买了第3票
1买了第2票
2买了第1票

添加方式四:代码块(注意位置)

    //在这个方法中,进行买票
    @Override
    public  void run() {

        //怎么买票? 票没就结束
        //循环买票
        while(true){

            // 票没有了,就进行停止
            if(ticket <= 0){
                break;
            }
    
            synchronized (this){
                //有票就进行卖票
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
                //买了一张,就少一张票
                ticket--;
            }
            
            
        }

    }

结果

1买了第100票
1买了第99票
1买了第98票
1买了第97票
1买了第96票
1买了第95票
1买了第94票
1买了第93票
1买了第92票
1买了第91票
1买了第90票
1买了第89票
1买了第88票
1买了第87票
1买了第86票
1买了第85票
1买了第84票
1买了第83票
1买了第82票
1买了第81票
1买了第80票
1买了第79票
1买了第78票
1买了第77票
1买了第76票
1买了第75票
1买了第74票
1买了第73票
1买了第72票
1买了第71票
1买了第70票
1买了第69票
1买了第68票
1买了第67票
1买了第66票
1买了第65票
1买了第64票
1买了第63票
1买了第62票
1买了第61票
1买了第60票
1买了第59票
1买了第58票
1买了第57票
1买了第56票
1买了第55票
1买了第54票
1买了第53票
1买了第52票
1买了第51票
1买了第50票
1买了第49票
1买了第48票
1买了第47票
1买了第46票
1买了第45票
1买了第44票
1买了第43票
1买了第42票
1买了第41票
1买了第40票
1买了第39票
1买了第38票
1买了第37票
1买了第36票
1买了第35票
1买了第34票
1买了第33票
1买了第32票
1买了第31票
1买了第30票
1买了第29票
1买了第28票
1买了第27票
1买了第26票
1买了第25票
1买了第24票
1买了第23票
1买了第22票
1买了第21票
1买了第20票
1买了第19票
1买了第18票
1买了第17票
1买了第16票
1买了第15票
1买了第14票
1买了第13票
1买了第12票
1买了第11票
1买了第10票
1买了第9票
1买了第8票
1买了第7票
1买了第6票
1买了第5票
1买了第4票
1买了第3票
1买了第2票
1买了第1票
2买了第0票
3买了第-1票

结果详解

		while(true){

            // 票没有了,就进行停止
            if(ticket <= 0){
                break;
            }

			可以发现,结果中有,10-1 ,出问题了是吧。
			
			分析:所以线程进入了这里,发现了同步代码块,进行排队+一个一个的获取锁,然后执行。
			那么,当ticket==1时,而且,3个线程都进入这里了,因为判断ticket <=0 的语句是在上面。
			直接执行即可。所以会有这种 10-1的结果。
            synchronized (this){
                //有票就进行卖票
                System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"票");
                //买了一张,就少一张票
                ticket--;
            }
        }

每次思考都会有收获。这次记录一下。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值