线程安全问题出现的原因及解决方案

接下来将通过一个案例,演示线程的安全问题:

电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “唐人街探案3”,本次电影的座位共100个(本场电影只能卖100张票)。

我们来模拟电影院的售票窗口,实现多个窗口同时卖 “葫芦娃大战奥特曼”这场电影票(多个窗口一起卖这100张票)需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟。

模拟票:

public class MyRunnable implements Runnable {
    int tickets = 100;// 4个窗口共同卖的票 共享变量

    @Override
    public void run() {
        // 实现卖票的操作
        // 死循环卖票
        while (true){
            // 当票卖完了,就结束
            if (tickets < 1){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":正在出售第"+tickets+"张票");
            tickets--;
        }
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        /*
            多行代码的问题:
                通过案例演示该问题: 电影院4个窗口卖票,卖的是同一份票,这份票总共有100张票
                分析:
                    1. 电影院4个窗口 相当于 4条线程
                    2. 电影院4个窗口 卖票的操作是一样 相当于每条线程的任务是一样的
         */
        // 电影院4个窗口  去卖票
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr,"窗口1");
        Thread t2 = new Thread(mr,"窗口2");
        Thread t3 = new Thread(mr,"窗口3");
        Thread t4 = new Thread(mr,"窗口4");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}![在这里插入图片描述](https://img-blog.csdnimg.cn/20200722214305778.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80OTQ5Njk4OA==,size_16,color_FFFFFF,t_70)


程序执行后,结果会出现的问题
在这里插入图片描述
发现程序出现了两个问题:

  1. 相同的票数,比如100这张票被卖了四回。
  2. 不存在的票,比如0票与-1票,-2票,是不存在的。

这种问题,几个窗口(线程)票数不同步了,这种问题称为线程不安全。

问题分析:
在这里插入图片描述

解决方案:

  • 同步代码块synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

格式:

synchronized(同步锁){
     需要同步操作的代码
}

同步锁:

对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

  1. 锁对象 可以是任意类型。
  2. 多个线程对象 要使用同一把锁。

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。

使用同步代码块解决代码:

public class MyRunnable implements Runnable {
    int tickets = 100;// 4个窗口共同卖的票 共享变量

    @Override
    public void run() {
        // 实现卖票的操作
        // 死循环卖票
        while (true) {
            // 当票卖完了,就结束
            // 加锁
            synchronized (this) {// mr钥匙
                if (tickets < 1) {
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":正在出售第" + tickets + "张票");
                tickets--;
            }

            // 释放锁
        }
    }
}

public class Test {
    //static Object lock  = new Object();
    public static void main(String[] args) {
       
        // 电影院4个窗口  去卖票
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr,"窗口1");
        Thread t2 = new Thread(mr,"窗口2");
        Thread t3 = new Thread(mr,"窗口3");
        Thread t4 = new Thread(mr,"窗口4");

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

当使用了同步代码块后,上述的线程的安全问题,解决了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值