Synchronized(Object),Synchronized(this),Synchronized(xxx.class)的区别

Synchronized(Object),Synchronized(this),Synchronized(xxx.class)的区别

一个转账例子可以让你明白这三个参数间的区别

Synchronized(Object)

import lombok.extern.slf4j.Slf4j;            //这是导入日志相关依赖包,后面有log.debug()相当于输出语句
import java.util.Random;
@Slf4j(topic = "c.ExerciseTransfer")
//转账测试类:1.开启两个线程,分别是账户a向账户b转账,账户b向账户a转账。
//		      2.查询各转账1000次后的结果
public class ExerciseTransfer {
    public static void main(String[] args) throws InterruptedException {
        Account a = new Account(1000);                            //创建账户a,账户余额1000
        Account b = new Account(1000);							  //创建账户b,账户余额1000
        //创建线程t1
        Thread t1 = new Thread(() -> {                                
            for (int i = 0; i < 1000; i++) {
                a.transfer(b, randomAmount());        
            }
        }, "t1");
         //创建线程t2
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                b.transfer(a, randomAmount());
            }
        }, "t2");
        //分别开启两个线程
        t1.start();
        t2.start();
        //等待两个线程运行完毕
        t1.join();
        t2.join();
        // 查看各转账1000次后的总金额
        log.debug("total:{}",(a.getMoney() + b.getMoney()));
    }
    // Random 为线程安全
    static Random random = new Random();
    // 随机 1~100
    public static int randomAmount() {
        return random.nextInt(100) +1;
    }
}
//账户实体类,内有转账方法
class Account {
    private int money;
    public Account(int money) {
        this.money = money;
    }
    public int getMoney() {
        return money;
    }
    public void setMoney(int money) {
        this.money = money;
    }
    //第一种情况:Synchronized(Object)
    static final Object obj = new Object();
    //转账方法
    public void transfer(Account target, int amount) {
        synchronized (obj){
            if (this.money > amount) {
                this.setMoney(this.getMoney() - amount);
                target.setMoney(target.getMoney() + amount);
            }
        }
    }
}

运行结果如下图:一直都是2000
在这里插入图片描述
结论:可以看出,只要拿到Object这个锁对象,这个线程就可以执行临界区代码块。

Synchronized(this)

  • 这里的代码我只改了转账方法里面的Object对象,改为this,这里只展现转账方法的代码
public void transfer(Account target, int amount) {
        synchronized (this){
            if (this.money > amount) {
                this.setMoney(this.getMoney() - amount);
                target.setMoney(target.getMoney() + amount);
            }
        }
    }

运行结果如下图:转账结果不等于2000,线程不安全。为什么会这样子呢?
在这里插入图片描述
解释:

  • 因为this表示当前对象,所以这把锁(Syschronized)只是锁住了这个当前对象。
  • 看上面测试类得知,我们有两个线程,一个线程(t1)是账户a转钱给账户b,一个线程(t2)是账户b转钱给账户a;
  • 这时,假设先执行t1线程,里面执行了 a.transfer(b, randomAmount());说明是a对象执行了这个转账方法,所以这把锁(Synchronized)锁住的是a对象;
  • 因为我们知道JVM对于方法的执行是分成多个指令的执行才完成的,拿值+运算+赋值等等指令;
  • 这时,如果线程t1执行了this.setMoney(this.getMoney() - amount);这段代码,但是,还没把赋值指令做完,t1线程时间片用完了,这里我假设a转出的前是30块,那么上面这个代码执行完后a账户的余额为970,但是这个赋值指令还没执行,所以a的账户余额还是1000。
  • 线程t2的时间片来了,线程t2也要执行转账方法,这时因为这把锁(Synchronized)锁住的是a对象,不会对b对象执行转账方法造成阻塞,b的转账方法会执行;
  • b对象的转账方法会执行下面两端代码
 this.setMoney(this.getMoney() - amount); 
 target.setMoney(target.getMoney() + amount);
  1. 假设b转出的钱为50,这是执行完第一行代码后,b的余额为950;
    重点来了:
    (1)这个b执行第二行代码,因为此时得出a余额target.getMoney()==1000;
    (2)1000+50(b转出)=1050,此时执行完b的转账方法后a的余额为1050;
    (3)接下来假设线程t2的时间片到了,轮到线程t1来执行转账方法时,因为上一次赋值操作还没做,所以这次继续赋值指令,把a账户的余额设置为了970 【出错的地方】 ,在执行最后一行代码 target.setMoney(target.getMoney() + amount);
    此时这条语句相当于:950(此时b的余额)+30(最开始a转出的钱)=980;

  2. 至此分别执行完了对线程t1和线程t2的一次转账操作,这时来看总额:

  3. a余额+b余额=970+980=1950;钱变少了。

结论:Synchronized(this)是对当前对象上锁,只要不是同一对象,就可以异步执行。

Synchronized(xxx.class)

问题:如何解决第二种方法带来的线程不安全呢?

  • 这就是第三种方法,a对象和b对象都是Account类的对象,所以只要这把锁锁住了这个类对象就可以同时锁住a对象和b对象。
    代码如下:
 public void transfer(Account target, int amount) {
        synchronized (Account.class){
            if (this.money > amount) {
                this.setMoney(this.getMoney() - amount);
                target.setMoney(target.getMoney() + amount);
            }
        }
    }

运行结果如下图:
在这里插入图片描述
结论:Synchronized(xxx.class)是对当前类上锁,不管是不是同一线程,只要是该类的对象,就会被上锁。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值