线程安全问题

线程不安全情况

以买票问题为例

买票问题

三个线程同时开启模拟三个窗口同时售票

public class Demo1 {
    public static void main(String[] args) {
        //线程不安全演示
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        @Override
        public void run() {
            while(count>0){
                //买票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println("出票成功,余票"+count);
            }
        }
    }
}

运行结果:
在这里插入图片描述
可以明显看到即使循环的条件是票数>0但余票还是出现了负数的情况,此时即出现了线程不安全问题。

解决方案

产生原因

单一的线程在while循环中读到的count数据不是“真实“的count数据,在执行while语句时count的值可能已经被其他的线程”插足“修改过了。
所以解决的根本目的是让某线程在“买票”的时间段内其他线程不得对数据进行处理,即让各个线程排队执行。
以下提供了三种让线程排队执行的方案。

同步代码块

public class Demo1 {
    public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        //锁对象
        private Object o = new Object();
        @Override
        public void run() {
            while(true){
                synchronized (o){
                    if (count>0){
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"出票成功,余票"+count);
                    }else {
                        break;
                    }

                }
            }
        }
    }
}

为什么要加if判断呢,因为上锁的其实就是if语句里面的操作。
运行结果:
在这里插入图片描述
可以看到明显没有票数小于0的情况了,但是为什么只有0号线程在卖票呢?其实这么说是不准确的,因为0号线程是最先开启的,它先执行上锁(上锁后其他线程不得操作数据),再解锁,但再解锁之后可以理解为0号线成离锁是最近的,因此它抢到的概率最大,而1号和2号线程抢到的概率较小。

同步方法

同步方法与同步代码块比较相似,皆是用到了synchronize方法来执行上锁操作。

public class Demo1 {
    public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        public synchronized boolean sale(){
            if (count>0){
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"出票成功,余票"+count);
                return true;
            }
            return false;
        }
        @Override
        public void run() {
            while(true){
                boolean flag =sale();
                if (!flag){
                    break;
                }
                }
            }
        }
    }

显式锁Lock

直接通过Lock类的上锁方法lock()和解锁方法unlock()进行手动上锁和解锁。
需要注意的是此方法创建的锁为显示锁,而上面的同步代码块儿和同步方法创建的皆是隐式锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo1 {
    public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        //创建锁
        Lock l = new ReentrantLock();
        @Override
        public void run() {
            while(true){
                //上锁
                l.lock();
                    if (count>0){
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"出票成功,余票"+count);
                    }else {
                        break;
                    }
                    //解锁
                    l.unlock();
                }
            }
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值