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

作者:遇安.112 时间:2022-02-28 02:03:24 

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

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

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

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

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

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

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){
       }
   }

第二种方法:同步方法

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

//同步方法
       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();//解锁
       }
   }
}

来源:https://blog.csdn.net/qq_62731133/article/details/124420818

标签:Java,线程,安全,同步
0
投稿

猜你喜欢

  • SpringBoot实现简单文件上传功能

    2021-07-22 21:52:49
  • 解决Spring boot整合mybatis,xml资源文件放置及路径配置问题

    2021-07-07 12:27:20
  • springboot html调用js无效400问题及解决

    2023-06-24 02:11:54
  • Android仿iOS侧滑退出当前界面功能

    2022-07-01 10:08:37
  • 深入理解Java并发编程之LinkedBlockingQueue队列

    2022-10-02 06:35:22
  • Android开发应用中Broadcast Receiver组件详解

    2023-04-25 09:35:35
  • 浅谈java常量池

    2023-11-18 09:43:50
  • C#中WPF内存回收与释放LierdaCracker的实现

    2022-11-13 00:15:44
  • java中Integer包装类装箱的一个细节详解

    2023-09-24 18:35:32
  • 使用Springboot自定义注解,支持SPEL表达式

    2023-11-20 01:18:58
  • java实现把对象数组通过excel方式导出的功能

    2022-07-01 13:20:09
  • Java正确使用访问修饰符的姿势

    2021-10-11 09:52:50
  • Jetpack Compose实现列表和动画效果详解

    2022-07-16 21:14:44
  • java启动jar包修改JVM默认内存问题

    2022-03-17 21:50:09
  • C#实现Stripe支付的方法实践

    2023-01-22 01:46:47
  • 使用Springboot自定义转换器实现参数去空格功能

    2021-11-12 03:36:29
  • Spring Boot提高开发效率必备工具lombok使用

    2022-05-21 08:22:38
  • C#实现自定义定时组件的方法

    2023-06-04 12:36:26
  • Java中的双重检查(Double-Check)详解

    2023-02-23 21:05:23
  • Android中使用Toast.cancel()方法优化toast内容显示的解决方法

    2021-12-14 05:17:03
  • asp之家 软件编程 m.aspxhome.com