多线程同步锁的使用范例

用一个卖票的例子来示范多线程操作一个对象时,同步锁的使用。

先新建一个SaleRunnable类实现Runnable接口,用于表示“卖票”过程:

    class SaleRunnable implements Runnable {

        private int ticket = 20;

        @Override
        public void run() {
            while(ticket > 0) {
                Log.d("Test", Thread.currentThread().getName() + "卖出了第" + (20-ticket+1) + "张票");
                ticket--;
            }
        }
    }

如上,一共有20张票,每卖出一张票,则打印卖出此票的线程名和票的序号。这个代码里没有加锁。

然后在主程序创建三个线程,传入同一个SaleRunnable对象,三个线程名分别为老王,老张,老李:

        SaleRunnable r = new SaleRunnable();
        Thread t1 = new Thread(r,"老王");
        Thread t2 = new Thread(r,"老张");
        Thread t3 = new Thread(r,"老李");

然后调用线程的start()方法启动三个线程:

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

运行结果如下:

08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第1张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第2张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第3张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第4张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第5张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第6张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第7张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第8张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第9张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第10张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第11张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第12张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第13张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第14张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第15张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第16张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第17张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第18张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第19张票
08-26 12:55:56.011 28300-28316/? D/Test: 老张卖出了第20张票
08-26 12:55:56.011 28300-28315/? D/Test: 老王卖出了第12张票

可见其结果是乱的,重复运行多次,结果不一,但是都没实现正常的卖票效果。为什么呢,因为三个线程在并行操作同一个对象,所以会出现老张的线程和老王的线程都卖出了第12张票的情况,但是这显然是不对的。

怎么办呢,修改下SaleRunnable类,加上锁试试:

    class SaleRunnable implements Runnable {

        private int ticket = 20;

        @Override
        public void run() {
            while(ticket > 0) {
                synchronized(this) {//把锁加在这里行不行呢?
                    Log.d("Test", Thread.currentThread().getName() + "卖出了第" + (20 - ticket + 1) + "张票");
                    ticket--;
                }
            }
        }
    }

如上面所示,把锁加在判断条件里面,运行结果:

08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第1张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第2张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第3张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第4张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第5张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第6张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第7张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第8张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第9张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第10张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第11张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第12张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第13张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第14张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第15张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第16张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第17张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第18张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第19张票
08-26 13:15:08.621 15256-15271/? D/Test: 老王卖出了第20张票
08-26 13:15:08.631 15256-15273/? D/Test: 老李卖出了第21张票
08-26 13:15:08.631 15256-15272/? D/Test: 老张卖出了第22张票

结果看,虽然卖票顺序对了,但卖多了也是不正常的,再改下:

    class SaleRunnable implements Runnable {

        private int ticket = 20;

        @Override
        public void run() {
            synchronized(this){//把锁加在循环判断外面,行不行呢?
            while(ticket > 0) {
                Log.d("Test", Thread.currentThread().getName() + "卖出了第" + (20 - ticket + 1) + "张票");
                ticket--;
            }
            }
        }
    }

运行结果:

08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第1张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第2张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第3张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第4张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第5张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第6张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第7张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第8张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第9张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第10张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第11张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第12张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第13张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第14张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第15张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第16张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第17张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第18张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第19张票
08-26 13:31:41.061 31687-31724/? D/Test: 老王卖出了第20张票

结果是没错,但是票全让老王给卖了,这也不正常,因为我们把锁加在整个卖票操作外面了,一旦老王开始卖票,其他人都没法卖了。再改:


    class SaleRunnable implements Runnable {

        private int ticket = 20;

        @Override
        public void run() {
            while(true) {//判断条件做了修改
                synchronized(this) {//锁加在循环判断里面,只有满足条件才能执行卖票操作
                    if (ticket > 0) {
                        Log.d("Test", Thread.currentThread().getName() + "卖出了第" + (20 - ticket + 1) + "张票");
                        ticket--;
                    } else {
                        break;
                    }
                }

                try {//这里的操作是为了方便线程间自动切换,如果不加的话,可能结果也是票全让老王线程给卖了
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

这样修改后结果正常了:

08-26 13:43:24.181 12176-12199/? D/Test: 老王卖出了第1张票
08-26 13:43:24.181 12176-12200/? D/Test: 老张卖出了第2张票
08-26 13:43:24.181 12176-12201/? D/Test: 老李卖出了第3张票
08-26 13:43:27.181 12176-12199/?D/Test: 老王卖出了第4张票
08-26 13:43:27.181 12176-12200/?D/Test: 老张卖出了第5张票
08-26 13:43:27.181 12176-12201/?D/Test: 老李卖出了第6张票
08-26 13:43:30.181 12176-12199/?D/Test: 老王卖出了第7张票
08-26 13:43:30.181 12176-12200/?D/Test: 老张卖出了第8张票
08-26 13:43:30.181 12176-12201/?D/Test: 老李卖出了第9张票
08-26 13:43:33.181 12176-12199/?D/Test: 老王卖出了第10张票
08-26 13:43:33.181 12176-12200/?D/Test: 老张卖出了第11张票
08-26 13:43:33.181 12176-12201/?D/Test: 老李卖出了第12张票
08-26 13:43:36.181 12176-12199/?D/Test: 老王卖出了第13张票
08-26 13:43:36.181 12176-12200/?D/Test: 老张卖出了第14张票
08-26 13:43:36.191 12176-12201/?D/Test: 老李卖出了第15张票
08-26 13:43:39.181 12176-12199/?D/Test: 老王卖出了第16张票
08-26 13:43:39.181 12176-12200/?D/Test: 老张卖出了第17张票
08-26 13:43:39.191 12176-12201/?D/Test: 老李卖出了第18张票
08-26 13:43:42.181 12176-12199/?D/Test: 老王卖出了第19张票
08-26 13:43:42.181 12176-12200/?D/Test: 老张卖出了第20张票

因此多个线程对同一个线程进行操作时,加锁是有效的解决冲突的方式,但是加到哪里一定要多多思考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值