• Java线程安全问题


    1、什么是线程安全问题

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2、用程序模拟线程安全问题

    在这里插入图片描述
    代码说明:

    1. Account代表账户类
    2. DrawThread代表线程类
    3. ThreadTest运行线程类

    Account类:

    package ThreadSave;
    
    public class Account {
        private double money;  //余额
        private String cardId; //卡号
    
        public Account() {
        }
    
        public Account(double money, String cardId) {
            this.money = money;
            this.cardId = cardId;
        }
    
        public double getMoney() {
            return money;
        }
    
        public void setMoney(double money) {
            this.money = money;
        }
    
        public String getCardId() {
            return cardId;
        }
    
        public void setCardId(String cardId) {
            this.cardId = cardId;
        }
    
        //取钱操作
        public void drawMoney(double money) {
            //先搞清楚谁在取钱
            String name = Thread.currentThread().getName();
            //开始进入取钱逻辑
            if (this.money>=money){
                System.out.println(name+"来取钱"+money+"成功!");
                this.money -= money;
                System.out.println(name+"来取钱后余额为:"+ this.money);
    
            }else {
                System.out.println(name+"来取钱---余额不足");
            }
        }
    }
    
    
    • 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
    • 43
    • 44
    • 45
    • 46

    DrawThread类:

    package ThreadSave;
    
    public class DrawThread extends Thread{
    
        private Account acc;
    
        public DrawThread(Account acc,String name){
            super(name);
            this.acc = acc;
        }
    
    
        @Override
        public void run() {
            //取钱
            acc.drawMoney(100000);
        }
    
    
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    ThreadTest类:

    package ThreadSave;
    
    //目标:模拟线程安全问题
    public class ThreadTest {
        public static void main(String[] args) {
    
            //1、创建一个账户对象,代表两个人的共享账号
            Account acc = new Account(100000,"ICBC-110");
    
            //2、创建2个线程,分别代表小明,小红,再去同一个账户对象取10万元
            new DrawThread(acc,"小明").start(); //代表小明
            new DrawThread(acc,"小红").start(); //代表小红
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行结果:
    在这里插入图片描述

    3、线程同步

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    1、同步代码块

    在这里插入图片描述
    共享资源加锁,我们上面案例的共享资源在Account类中,因此我们需要对Account中的代码进行加锁,修改后的代码:
    加锁快捷键:选中取钱部分代码----》Ctrl+Alt+T选择synchronized

    package ThreadSave;
    
    public class Account {
        private double money;  //余额
        private String cardId; //卡号
    
        public Account() {
        }
    
        public Account(double money, String cardId) {
            this.money = money;
            this.cardId = cardId;
        }
    
        public double getMoney() {
            return money;
        }
    
        public void setMoney(double money) {
            this.money = money;
        }
    
        public String getCardId() {
            return cardId;
        }
    
        public void setCardId(String cardId) {
            this.cardId = cardId;
        }
    
        //取钱操作
        public void drawMoney(double money) {
            //先搞清楚谁在取钱
            String name = Thread.currentThread().getName();
            //1、判断余额是否足够
            //-------------------------------------------------------------------------------------
            //加锁
            synchronized ("海梦") {
                //开始进入取钱逻辑
                if (this.money>=money){
                    System.out.println(name+"来取钱"+money+"成功!");
                    this.money -= money;
                    System.out.println(name+"来取钱后余额为:"+ this.money);
    
                }else {
                    System.out.println(name+"来取钱---余额不足");
                }
            }
        }
    }
    
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    运行结果:
    在这里插入图片描述
    注意事项:在上面的锁中,我们随意使用了一个 "海梦"来完成,这样会产生一些问题,因为它可锁住任何线程进行访问。
    我们需要把 “海梦” 改为 this,这样就没有问题了。

    如果还静态锁的话,我们应该使用class作为锁,示例代码:

    //静态方法加锁
        public static void test(){
            synchronized (Account.class){
                
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    2、同步方法

    在这里插入图片描述

    对方法加锁:
    取钱操作
    public synchronized void drawMoney(double money)
    具体代码:

    package ThreadSave;
    
    public class Account {
        private double money;  //余额
        private String cardId; //卡号
    
        public Account() {
        }
    
        public Account(double money, String cardId) {
            this.money = money;
            this.cardId = cardId;
        }
    
        public double getMoney() {
            return money;
        }
    
        public void setMoney(double money) {
            this.money = money;
        }
    
        public String getCardId() {
            return cardId;
        }
    
        public void setCardId(String cardId) {
            this.cardId = cardId;
        }
    
        //取钱操作
        public synchronized void drawMoney(double money) {
            //先搞清楚谁在取钱
            String name = Thread.currentThread().getName();
            //1、判断余额是否足够
            //this正好代表共享资源
                //开始进入取钱逻辑
                if (this.money>=money){
                    System.out.println(name+"来取钱"+money+"成功!");
                    this.money -= money;
                    System.out.println(name+"来取钱后余额为:"+ this.money);
    
                }else {
                    System.out.println(name+"来取钱---余额不足");
                }
        }
    }
    
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在这里插入图片描述

    3、Lock锁

    在这里插入图片描述
    在Account类中创建一个锁对象
    然后在特定的位置(共享资源位置)加锁
    执行完后再解锁
    相关代码:

    package ThreadSave;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Account {
        private double money;  //余额
        private String cardId; //卡号
    
        //创建一个锁对象
        private final Lock lk = new ReentrantLock();
    
        public Account() {
        }
    
        public Account(double money, String cardId) {
            this.money = money;
            this.cardId = cardId;
        }
    
        public double getMoney() {
            return money;
        }
    
        public void setMoney(double money) {
            this.money = money;
        }
    
        public String getCardId() {
            return cardId;
        }
    
        public void setCardId(String cardId) {
            this.cardId = cardId;
        }
    
        //取钱操作
        public void drawMoney(double money) {
            //先搞清楚谁在取钱
            String name = Thread.currentThread().getName();
            //加锁
            lk.lock();
            //1、判断余额是否足够
                //开始进入取钱逻辑
                if (this.money>=money){
                    System.out.println(name+"来取钱"+money+"成功!");
                    this.money -= money;
                    System.out.println(name+"来取钱后余额为:"+ this.money);
    
                }else {
                    System.out.println(name+"来取钱---余额不足");
                }
            //解锁
            lk.unlock();
        }
    }
    
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    注意事项:如果在lk.lock()和lk.unlock()中间出现bug,那么就会解锁失败,因此我们应该把中间的代码放在try-catch-finally中
    示例代码:

    try {
                //加锁
                lk.lock();
                //1、判断余额是否足够
                //开始进入取钱逻辑
                if (this.money>=money){
                    System.out.println(name+"来取钱"+money+"成功!");
                    this.money -= money;
                    System.out.println(name+"来取钱后余额为:"+ this.money);
    
                }else {
                    System.out.println(name+"来取钱---余额不足");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //解锁
                lk.unlock();
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

  • 相关阅读:
    Ubuntu22.04系统 Cgroup v2 切换成v1
    基于SDN环境下的DDoS异常攻击的检测与缓解--实验
    linux 安装 jdk 环境
    iOS 提高Xcode运行速度
    ppt复现CVPR顶会流程图
    查看文件的二进制数据
    2020年09月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试
    C++内存模型与名称空间总结,看这一篇就够了
    【Spring源码】自动注入·类型:autowireByType()详解
    哈希算法:哈希算法在分布式系统中有哪些应用?
  • 原文地址:https://blog.csdn.net/N16696796429/article/details/133824023