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线程同步内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring bean 加载执行顺序实例解析

    Spring bean 加载执行顺序实例解析

    这篇文章主要介绍了Spring bean 加载执行顺序实例解析,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Java基于jeeplus vue实现简单工作流过程图解

    Java基于jeeplus vue实现简单工作流过程图解

    这篇文章主要介绍了Java基于jeeplus vue实现简单工作流过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • IntelliJ IDEA各种图标的含义

    IntelliJ IDEA各种图标的含义

    这篇文章主要介绍了IntelliJ IDEA各种图标的含义,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • java实现猜拳游戏试题

    java实现猜拳游戏试题

    这篇文章主要为大家详细介绍了java实现猜拳游戏试题,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-03-03
  • java返回前端树形结构数据的2种实现方式

    java返回前端树形结构数据的2种实现方式

    近期项目有个需求,需要将组织机构数据拼成树型结构返回至前端,下面这篇文章主要给大家介绍了关于java返回前端树形结构数据的2种实现方式,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-05-05
  • java实现识别二维码图片功能

    java实现识别二维码图片功能

    这篇文章主要为大家详细介绍了java实现识别二维码图片功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 基于Springboot+Netty实现rpc的方法 附demo

    基于Springboot+Netty实现rpc的方法 附demo

    这篇文章主要介绍了基于Springboot+Netty实现rpc功能,在父项目中引入相关依赖结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-02-02
  • Java设计模式之抽象工厂模式详解

    Java设计模式之抽象工厂模式详解

    这篇文章主要介绍了Java设计模式之抽象工厂模式详解,抽象工厂是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的、不同等级的产品的模式结构,需要的朋友可以参考下
    2023-09-09
  • springboot连接Redis的教程详解

    springboot连接Redis的教程详解

    这篇文章主要介绍了springboot连接Redis的教程详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • SpringCloud之微服务容错的实现

    SpringCloud之微服务容错的实现

    这篇文章主要介绍了SpringCloud之微服务容错的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05

最新评论