软件编程
位置:首页>> 软件编程>> java编程>> Java使用线程同步解决线程安全问题详解

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

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

标签:Java,线程,安全,同步

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

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

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

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

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

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

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

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com