Java使用线程同步解决线程安全问题详解

 更新时间:2022年05月07日 11:45:47   作者:遇安.112  
线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况

第一种方法:同步代码块:

作用:把出现线程安全的核心代码上锁

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

锁对象要求:理论上,锁对象只要对于当前同时执行的线程是同一个对象即可

缺点:会干扰其他无关线程的执行

所以,这种只是理论上的,了解即可,现实中并不会这样用

public class 多线程_4线程同步 {
    public static void main(String[] args) {
        //定义线程类,创建一个共享的账户对象
        account a=new account("abc",10000);
        //创建两个取钱的线程对象
        new drawthread(a,"小明").start();
        new drawthread(a,"小红").start();
    }
}
//取钱的线程类
class drawthread extends Thread{
    //接收处理的账户对象
    private account acc;
    public drawthread(account acc,String name){
        super(name);
        this.acc=acc;
    }
    public void run(){
        //取钱
        acc.drawmoney(10000);
    }
}
class account{
    private String cartId;
    private double money;//账户余额
    public account() {
    }
    public account(String cartId, double money) {
        this.cartId = cartId;
        this.money = money;
    }
    public String getCartId() {
        return cartId;
    }
    public void setCartId(String cartId) {
        this.cartId = cartId;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
    public void drawmoney(double money) {
        //先获取是谁来取钱,线程名即是人名
        String name=Thread.currentThread().getName();
        //同步代码块
        //作用:把出现线程安全的核心代码上锁
        //原理:每次只能一个线程进入,执行完毕后自行解锁,其他线程才能进来执行
        //锁对象要求:理论上,锁对象只要对于当前同时执行的线程是同一个对象即可
        //缺点:会干扰其他无关线程的执行
        synchronized ("遇安") {//"锁名自取,无意义"
            //判断账户是否够钱
            if(this.money>=money){
                //取钱
                System.out.println(name+"来取钱成功,取了:"+money);
                //更新金额
                this.money-=money;
                System.out.println(name+"取钱后剩余:"+this.money);
            }else{
                //余额不足
                System.out.println(name+"来取钱,但余额不足!");
            }
        }
    }
}

规范上:建议使用共享资源作为锁对象

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

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

        //接上文代码
//实例化方法建议使用this作为锁对象
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作为锁对象
//每次只有一个线程能锁这个类,而类也是唯一的
  public static void run(){
        synchronized(account.class){
        }
    }

第二种方法:同步方法

    //同步方法
        public synchronized 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+"来取钱,但余额不足!");
            }
    }

那么同步代码块和同步方法哪个好一点呢?

答案是:同步代码块

因为同步代码块锁的范围更小一点,同步方法锁的范围更大一点

但其实在现实中同步方法用的更多一点,因为代码简洁好写一点,更方便

第三种方法:Lock锁

JDK5后出现,更加灵活方便

Lock是接口不能直接实例化,我们需要采用它的实现类ReentrantLock来构建Lock锁对象

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class 多线程_4线程同步Lock锁 {
    public static void main(String[] args) {
        //定义线程类,创建一个共享的账户对象
        account a=new account("abc",10000);
        //创建两个取钱的线程对象
        new drawthread(a,"小明").start();
        new drawthread(a,"小红").start();
    }
}
//取钱的线程类
class drawthread2 extends Thread{
    //接收处理的账户对象
    private account acc;
    public drawthread2(account acc,String name){
        super(name);
        this.acc=acc;
    }
    public void run(){
        //取钱
        acc.drawmoney(10000);
    }
}
class account2{
    private String cartId;
    private double money;//账户余额
    //final修饰后:锁对象是唯一的和不可替换的
//Lock是接口不能直接实例化,我们需要采用它的实现类ReentrantLock来构建Lock锁对象
    private final Lock lock=new ReentrantLock();
    public account2() {
    }
    public account2(String cartId, double money) {
        this.cartId = cartId;
        this.money = money;
    }
    public String getCartId() {
        return cartId;
    }
    public void setCartId(String cartId) {
        this.cartId = cartId;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
    public void drawmoney(double money) {
        //先获取是谁来取钱,线程名即是人名
        String name=Thread.currentThread().getName();
        lock.lock();//上锁
        //判断账户是否够钱
        try {
            if(this.money>=money){
                //取钱
                System.out.println(name+"来取钱成功,取了:"+money);
                //更新金额
                this.money-=money;
                System.out.println(name+"取钱后剩余:"+this.money);
            }else{
                //余额不足
                System.out.println(name+"来取钱,但余额不足!");
            }
            //防止代码出现bug而不能解锁
        } finally {
            lock.unlock();//解锁
        }
    }
}

到此这篇关于Java使用线程同步解决线程安全问题详解的文章就介绍到这了,更多相关Java线程同步内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中springboot搭建html的操作代码

    Java中springboot搭建html的操作代码

    这篇文章主要介绍了Java中springboot搭建html的相关操作,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • java开源区块链初始化创世区块jdchain服务搭建

    java开源区块链初始化创世区块jdchain服务搭建

    这篇文章主要介绍了java开源区块链初始化创世区块jdchain的服务搭建步骤,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • RateLimit-使用guava来做接口限流代码示例

    RateLimit-使用guava来做接口限流代码示例

    这篇文章主要介绍了RateLimit-使用guava来做接口限流代码示例,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • Java中判断对象是否为空的方法的详解

    Java中判断对象是否为空的方法的详解

    这篇文章主要介绍了Java中判断对象是否为空的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Java并发问题之乐观锁与悲观锁

    Java并发问题之乐观锁与悲观锁

    这篇文章主要介绍了Java并发问题之乐观锁与悲观锁,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • mybatis Reflector反射类的具体使用

    mybatis Reflector反射类的具体使用

    Reflector类是MyBatis反射模块的核心,负责处理类的元数据,以实现属性与数据库字段之间灵活映射的功能,本文主要介绍了mybatis Reflector反射类的具体使用,感兴趣的可以了解一下
    2024-02-02
  • mybatis-plus分页查询的实现实例

    mybatis-plus分页查询的实现实例

    页查询是一项常用的数据库查询方法,本文主要介绍了mybatis-plus分页查询的实现实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-06-06
  • Java基础之详解HashSet的使用方法

    Java基础之详解HashSet的使用方法

    今天给大家带来的是关于Java基础的相关知识,文章围绕着HashSet的使用方法展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • 通过ibatis解决sql注入问题

    通过ibatis解决sql注入问题

    这篇文章主要介绍了通过ibatis解决sql注入问题,需要的朋友可以参考下
    2017-09-09
  • Java7到Java17之Switch语句进化史示例详解

    Java7到Java17之Switch语句进化史示例详解

    这篇文章主要为大家介绍了Java7到Java17之Switch语句进化史示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01

最新评论