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
    评论
### 回答1: 您好!如果我理解正确的话,您的问题是关于JMU Java 07多线程同步访问的。以下是我的回答: 多线程编程是指在一个程序中同时执行多个线程的编程技术。多线程编程在提高程序性能、增强程序交互性等方面有很大的优势,但也会引入一些问题,比如线程安全问题。 当多个线程同时访问共享资源时,可能会出现线程不安全的问题,比如数据竞争、死锁等。为了避免这些问题,我们需要使用同步机制来确保线程安全Java中提供了多种同步机制,比如synchronized关键字、ReentrantLock类、Semaphore类等。这些同步机制都可以用来保证多线程访问共享资源的安全性。 其中,synchronized是最常用的同步机制之一。在Java中,synchronized关键字可以用来修饰方法或代码块,以实现同步访问共享资源。synchronized可以保证在同一时间只有一个线程可以访问共享资源,从而避免了数据竞争等线程安全问题。 除了synchronized之外,Java还提供了一些其他的同步机制,比如ReentrantLock类和Semaphore类。ReentrantLock类提供了比synchronized更灵活的锁机制,可以实现可重入锁、公平锁等功能。Semaphore类可以控制同时访问共享资源的线程数量,从而避免资源被过度占用的问题。 总的来说,多线程编程是一项复杂而重要的技术,需要仔细研究和实践。在实际开发中,我们需要根据具体的需求选择合适的同步机制来确保多线程访问共享资源的安全性。 ### 回答2: 多线程编程是在当前计算机领域中最为常见的技术之一,它可以利用计算机中的多核处理器来使程序运行更加高效。但是,多线程编程中可能会出现的最大问题就是线程安全,因为线程之间可能会访问相同的资源,从而导致竞态条件。 在Java中,可以通过使用synchronized关键字来实现同步访问,从而避免线程安全问题。synchronized关键字可以用于两种不同的情形:同步方法和同步块。在同步方法中,方法是同步的,即每个线程在执行该方法时都需要获取该对象的锁,如果该锁已经被其他线程获取,则需要等待直到此锁被释放。在同步块中,需要手动指定锁,即每个线程在执行同步块时需要获取该指定锁,其他线程如果需要访问该代码块中的共享资源也需要获取该指定锁,这样就保证了该代码块中的所有共享资源的同步访问。 除了synchronized关键字外,Java还提供了其他一些同步机制来实现线程安全,如ReentrantLock类和CountDownLatch类等。ReentrantLock类可以实现更为灵活的同步访问控制,但需要手动释放锁;而CountDownLatch类则用于同步一个或多个线程,使这些线程在某个条件满足之前一直处于等待状态。 在进行多线程编程时,应该尽量避免对同步访问造成瓶颈,应该通过减小同步代码块的范围等方式来提高程序的效率。此外,多线程编程时还应该进行线程安全性的测试,以确保程序能够正确地运行。 ### 回答3: 在Java中,多线程是一种非常常见的编程方式。由于多线程的特点,对共享资源的访问会出现竞争的情况,这种竞争可能会导致数据不一致或程序异常等问题。因此,在多线程编程中,我们需要采取一些措施来保证共享资源的访问能够正确、有序地进行,这就是同步机制。 同步机制包括两种方式:锁和信号量。锁是最基本的同步机制。锁有两种类型:互斥锁(Mutex)和读写锁(ReadWriteLock)。互斥锁用于保护共享资源,保证同一时间只有一个线程可以访问它,其他线程需要等待锁释放后才能继续访问。读写锁用于读写分离场景,提高了多线程访问共享资源的并发性。读写锁支持多个线程同时读取共享资源,但只允许一个线程写入共享资源。 信号量是一种更加高级的同步机制。信号量可以用来控制并发线程数和限制访问共享资源的最大数量。在Java中,Semaphore类提供了信号量的实现。Semaphore可以控制的线程数量可以是任意的,线程可以一起执行,也可以分批执行。 除了锁和信号量,Java还提供了一些其他同步机制,比如阻塞队列、Condition等。阻塞队列是一种特殊的队列,它支持线程在插入或者删除元素时阻塞等待。Condition是一种锁的增强,它可以让线程在某个特定条件下等待或者唤醒。 在多线程编程中,使用同步机制需要注意以下几点。首先,同步机制要尽可能的保证资源访问的公平性,避免因为某些线程执行时间过长导致其他线程等待时间过长。其次,同步机制要尽可能的避免死锁的发生,尤其要注意线程之间的依赖关系。最后,同步机制的实现要尽可能地简单,避免过于复杂的代码实现带来的维护成本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

凌晨四点半sec

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

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

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

打赏作者

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

抵扣说明:

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

余额充值