Java-线程安全问题、线程同步

个人简介

  • 大家好,我是翰慧腾。一名正在努力学JAVA的大一小白,本文章为初学的笔记,希望各位多多指教。💙
  • 欢迎点赞+收藏+留言💜
  • 自己发光,而不是被谁照亮🧡

一、线程安全问题

概述:多个线程同时操作同一个共享资源(修改其资源数据)的时候可能会出现业务安全问题,称为线程安全问题。

案例:小明和小红是一对夫妻,他们有一个共同账户,余额是10万元,模拟2人同时去取钱10万。

 

/**
 * @author hanhan
 * date 2022/4/17 15:54
 * 努力已经来不及了,你得拼命
 */
public class Thread_safety {
    public static void main(String[] args) {
        Account a1 = new Account("111222333", 1000000);
        //创建两个线程对象,代表小明和小红同时进来
       new DrawThread_1(a1,"小明").start();
        new DrawThread_1(a1,"小红").start();

    }
}
class Account{
    private String cardId;
    private double money;

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    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+"来取钱,余额不足了");
        }
    }
}
class DrawThread_1  extends Thread{
    private Account a1;
    public DrawThread_1(Account a1,String name){
        super(name);
        this.a1=a1;

    }
    @Override
    public void run() {
        //取钱
        a1.drawMoney(1000000);
    }
}

存在问题:账户1000000元,却两个人取了2000000元。

二、线程同步(为了解决线程安全问题)

就针对以上的取钱问题,解决办法:让多个线程实现先后依次访问共享资源

线程同步的核心思想:加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才可以进来。

方式一

同步代码块:把出现线程安全问题的核心代码给加上锁。

原理:每次只能一个线程进入,执行完毕后自动解锁,其它线程才可以进来执行

锁对象要求:锁对象只要对于当前同时执行的线程来说是同一个对象即可。 

锁对象的规范要求:

1.建议使用共享资源作为锁对象

2.对于实例方法建议使用this作为锁对象

3.对于静态方法建议使用字节码(类名.class)对象作为锁对象

/**
 * @author hanhan
 * date 2022/4/17 15:54
 * 努力已经来不及了,你得拼命
 */
public class Thread_safety {
    public static void main(String[] args) {
        Account a1 = new Account("111222333", 1000000);
        //创建两个线程对象,代表小明和小红同时进来
       new DrawThread_1(a1,"小明").start();
        new DrawThread_1(a1,"小红").start();

    }
}
class Account{
    private String cardId;
    private double money;

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public void drawMoney(double money) {
      //获取取钱人
       String name= Thread.currentThread().getName();
       //判断余额是否够
        synchronized (this) {
            if(this.money>=money){
                System.out.println(name+"取钱成功,吐出"+money);
                //更新余额
                this.money-=money;
                System.out.println(name+"取钱后剩余"+this.money);
            }else{
                System.out.println(name+"来取钱,余额不足了");
            }
        }
    }
}
class DrawThread_1  extends Thread{
    private Account a1;
    public DrawThread_1(Account a1,String name){
        super(name);
        this.a1=a1;

    }
    @Override
    public void run() {
        //取钱
        a1.drawMoney(1000000);
    }
}

方式二

同步方法:把出现线程安全问题的核心方法给加上锁

原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以来执行。

同步方法底层原理:同步方法底层也是有隐式锁对象的,只是锁的范围是整个方法代码;如果方法是实例方法:同步方法默认用this作为锁的对象,但是代码要高度面向对象。如果是方法是静态方法:同步方法默认用类名.class作为锁的对象。

方式一和方式二的比较:同步代码块锁的范围更小,同步方法锁的范围更大

 方式三 Lock锁

概述:Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作;Lock是接口,不能直接实例化,使用它的实现类ReentrantLock来构建Lock锁对象。

 

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

/**
 * @author hanhan
 * date 2022/4/17 15:54
 * 努力已经来不及了,你得拼命
 */
public class Thread_safety {
    public static void main(String[] args) {
        Account a1 = new Account("111222333", 10000000);
        //创建两个线程对象,代表小明和小红同时进来
       new DrawThread_1(a1,"小明").start();
        new DrawThread_1(a1,"小红").start();

    }
}
class Account{
    private String cardId;
    private double money;
    //定义一个锁
    private final Lock l=new ReentrantLock();
    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public  void drawMoney(double money) {
      //获取取钱人
       String name= Thread.currentThread().getName();
       //判断余额是否够
        try {
            l.lock();//上锁
            if(this.money>=money){
                System.out.println(name+"取钱成功吐出"+money);
                //更新余额
                this.money-=money;
                System.out.println(name+"取钱后剩余"+this.money);
            }else{
                System.out.println(name+"来取钱,余额不足了");
            }
        } finally {
            l.unlock();//解锁
        }
    }
    }
class DrawThread_1  extends Thread{
    private Account a1;
    public DrawThread_1(Account a1,String name){
        super(name);
        this.a1=a1;

    }
    @Override
    public void run() {
        //取钱
        a1.drawMoney(100000);
    }
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌晨四点半sec

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值