• 02 线程安全问题


    卖票

    • 案例需求
      某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
    • 实现步骤
      • 定义一个类SellTicket实现Runnable接口,里面定义一个成员变量:private int tickets = 100;
      • 在SellTicket类中重写run()方法实现卖票,代码步骤如下
      • 判断票数大于0,就卖票,并告知是哪个窗口卖的
      • 卖了票之后,总票数要减1
      • 票卖没了,线程停止
      • 定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下
      • 创建SellTicket类的对象
      • 创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称
      • 启动线程
    • 代码演示
        private  int tickets = 100;
        @Override
        public void run() {
            while (true) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    tickets--;
                    System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + tickets + "张票");
                } else {
                    break;
                }
            }
        }
    }
    
    public class SellTicketDemo {
        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.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    卖票案例的问题

    • 买票出现了问题
      • 相同的票出现了多次
        在这里插入图片描述
      • 出现了负数的票
        在这里插入图片描述
    • 问题产生原因
      线程执行的随机性导致的,可能在卖票过程中丢失CPU的执行权,导致出现问题

    同步代码块解决数据安全问题

    • 安全问题出现的条件
      • 是多线程数据
      • 是共享数据
      • 有多条语句操作共享数据
    • 如何解决多线程安全问题
      • 基本思想:让程序没有安全问题的环境
    • 如何实现
      • 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
      • Java提供了同步代码块的方式来解决
    • 同步代码块格式
    synchronized(任意对象) { 
    	多条语句操作共享数据的代码 
    }
    
    • 1
    • 2
    • 3
    • synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁

    • 同步的好处和弊端

      • 好处:解决了多线程的数据安全问题
      • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
    • 代码演示

    public class SellTicket implements Runnable{
        private int tickets = 100;
        private Object obj = new Object();
        @Override
        public void run() {
            while (true){
                synchronized (obj){
                    if (tickets > 0){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
    
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                        tickets--;
                    }else {
                        break;
                    }
                }
            }
        }
    }
    
    public class Test {
        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.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    同步方法解决数据安全问题

    • 同步方法的格式
      同步方法:就是把synchronized关键字加到方法上
    修饰符 synchronized 返回值类型 方法名(方法参数) { 
    	方法体;
    }
    
    • 1
    • 2
    • 3

    同步方法的锁对象是什么呢?
    ​ this

    • 静态同步方法
      同步静态方法:就是把synchronized关键字加到静态方法上
    修饰符 static synchronized 返回值类型 方法名(方法参数) { 
    	方法体;
    }
    
    • 1
    • 2
    • 3

    同步静态方法的锁对象是什么呢?
    ​ 类名.class

    public class SellTicket implements Runnable{
        private int tickets = 100;
        @Override
        public void run() {
            while (true){
                pick();
            }
    
        }
    
        public synchronized void pick(){
            if (tickets > 0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                tickets--;
            }else {
                System.exit(0);
            }
        }
    }
    
    public class Test {
        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.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    死锁

    • 概述
      线程思索是由于两个或者多个线程相互持有对方所需要的资源,导致这些线程处于等待状态而无法前往执行
    • 什么情况下会产生死锁
      1. 资源有限
      2. 同步嵌套
    • 代码演示
    public class Demo {
        public static void main(String[] args) {
            Object objA = new Object();
            Object objB = new Object();
    
            new Thread(() -> {
               while (true){
                   synchronized (objA){
                       synchronized (objB){
                           System.out.println("小明同学正在走路");
                       }
                   }
               }
            }).start();
    
            new Thread(() -> {
                while (true){
                    synchronized (objB){
                        synchronized (objA){
                            System.out.println("小红同学正在走路");
                        }
                    }
                }
            }).start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • 相关阅读:
    车载GNSS/INS/LiDAR坐标系定义与理解
    IP协议的相关特性
    几率波量子雷达/反事实量子通信
    python学习之基本语法---语法规则---函数的基本用法(三)day9
    Elasticsearch入门
    Xilinx FPGA 7系列 GTX/GTH Transceivers (3) Aurora 8b10b
    NX上配置TLD的环境---对opencv的版本没有要求
    软件测试流程是什么?这题我不会啊
    GitLab SAST:如何将Klocwork与GitLab一起使用
    找不到msvcr110.dll丢失的解决方法-常见修复方法分享
  • 原文地址:https://blog.csdn.net/m0_59620032/article/details/127428525