synchronized详解

synchronized的作用

synchronized是java中的一个关键字,可以加在静态方法、非静态方法、代码块上,把指定区域的代码进行同步操作。所谓“同步”说的是对线程进行同步:多个线程走到这块代码区域前,必须一个一个来,A线程执行完,B线程才能进来执行,也就是说线程串行化。

synchronized锁的范围

上面我们知道synchronized可以加在静态方法上、非静态方法上、代码块上。加在不同的地方意味着synchronized的monitor对象不同,也就是说synchronized用来进行同步的那个锁不同,而锁最重要的不同点在于锁的范围不同!

如果加在静态方法上,则是以当前所在类的class对象作为锁,一个类的class对象在一个JVM中只有一个,所以静态方法上如果加了synchronized,则在这个JVM中执行的所有线程如果想要来执行这个静态方法的代码,那么全体来抢,只有一把锁。

如果是加在非静态方法上,则是对当前所在对象作为锁,如果是被Spring管理的Bean,又是默认的singleton作用域的话,那么在一个JVM中也只有一个,和上面的class对象锁一样;但如果不是singleton的Bean,或者根本就不是被Spring管理的一个new出来的对象,那么此时就要注意了!看如下伪代码:

public class Key{
    public synchronized void intoBathRoom() {
        System.out.println("take a bath");
    }
}


public class Person{
    private Key key = new Key();

    public static void main(String[] args){
        Thread threadA = new Thread(()->{
            Person tom = new Person();
            tom.intoBathRoom();
        });
        
        Thread threadB = new Thread(()->{
            Person jerry = new Person();
            jerry.intoBathRoom();
        });
        
    }
}

上述伪代码中,线程A和线程B分别创建了一个Person,而这两个Person里面分别有属于自己的Key对象,这是两个不同的key对象,在调用key.intoBathRoom()时,便是不同的两把锁,所以线程A和线程B不会进行同步!

如果是加在代码块上,需要直接指明monitor,也就是锁对象:

public class Key{
    public void intoBathRoom() {
        synchronized(this){
            System.out.println("take a bath");
        }
    }
}

同样的道理,需要明确:锁对象在JVM中是否只有一个?如果不是,那么锁对象在什么时候创建的?锁住的范围有多大?

注意:

A.class对象和通过Anew出来的一个对象a,不是一个对象,所以不是一把锁;同理,Anew出来的对象a,和new出来的对象aa,也不是一个对象,所以也不是一把锁。

synchronized锁的特性

非公平锁、悲观锁、可重入锁、对象锁/类锁

以上说了这么多的锁,其实都不是锁实现,只不过是锁特性而已。

非公平锁:从名字就可以看出,非公平锁是不公平的,不遵循“先来后到”的原则,谁抢到就算谁的。而与非公平锁相反的就是公平锁了,也就说每个线程来了,都得排队,实现“先来后到”。

悲观锁:你定义了一块代码,这块代码可能只有一个时间只有一个线程在执行,也有可能一个时间点两个线程都来执行。悲观的选择就是:假设肯定会存在一个时间点两个线程都来执行的情况,所以不管会不会发生这样的情况,每个线程来了进去之前都加上一把锁。与悲观锁对应的就是乐观锁了,乐观锁的选择是:假设一个时间点两个线程都来执行的情况应该不多,即使发生这样的情况,我就加个CAS判断,让外面那个线程多来重试几次,直到成功为止。

可重入锁:线程A第一个进来获得了锁,没有释放这个锁的情况下,线程A又一次进来了,此时如果是可重入锁的话,线程A依然可以进来,不过可重入锁内部会有一个计数器,你线程A进来了多少次我这儿都记着呢,最后你就得释放多少次。与可重入锁相反的就是不可重入锁了,只能进入一次,除非释放掉,才能再一次进入。

对象锁/类锁:这个就是说synchronized的monitor对象是个类的class对象,还是个类new出来的一个普通对象。

synchronized原理

synchronized底层实现机制是通过JVM源码实现的,而JVM源码是通过C语言来写的。我们这里就白话一下:

把monitor对象作为锁,monitor对象中有计数器,可以累加或递减,线程与指定的monitor对象交互,决定线程是否可以获取锁。

获取锁时到指定的monitor对象中去查看,看计算器是否为0,如果是0的话,获取锁成功,如果不是0,线程被挂起,阻塞等待;当同一个线程再次进入synchronized代码块(JVM指令:monitor enter),会重新去指定的monitor对象中获取锁,monitor发现是持有锁的线程,放行,计算器加1,当该线程每次执行完synchronized代码块中的代码出去时(JVM指令:monitor exit),计数器减1,减为0 后锁释放。

以上说的是重量级锁的原理。

synchronized的优化:

在Java 6中为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在Java中,锁共有4种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。

具体请参考我的另外一篇博客:Synchronized锁机制_jerry_dyy的博客-CSDN博客

注意事项:synchronized可以做到的锁的最大范围就是一个JVM,实际中的线上系统为了保证高可用,每个应用或服务,至少会部署两个实例。此时得考虑衡量一下synchronized是否可以满足真正的需求!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值