java高并发之魂:Synchronize

时间:2024-04-10 12:25:37

synchronize在高并发领域可谓是元老级别了,博主最近整理了一下synchronize的用法,和简单的概念。

概念

对象锁:包括 方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)

类锁:指synchronized 修饰静态的方法或指定锁对象为Class对象

用法

同步代码块锁

用this锁:

java高并发之魂:Synchronize

自己指定锁:

java高并发之魂:Synchronize

java高并发之魂:Synchronize

用this时,2个线程用的是同一把锁,可以保证串行执行。自定义2把锁(lock1,lock2)时,由于是2个锁对象,无法保证2个线程串行执行

方法锁

sysnchronized 修饰普通方法

java高并发之魂:Synchronize

可以保证串行执行

类锁

java类可能有很多个对象,但只有1个class对象,所以所谓的类锁其实就是Class对象锁而已。(不同实例只能在同一时刻被一个对象拥有)

sysnchronized加在static方法上

java高并发之魂:Synchronize

sysnchronized(*.class)代码块

java高并发之魂:Synchronize

均可实现代码的串行化执行

常见面试题

1.两个线程同时访问一个对象的同步方法

答案:可以实现串行化执行,因为用的是同一个对象锁,为同一把锁

2.两个线程访问的是两个对象的同步方法

答案:不可以实现串行化执行。因为锁对象不同,互不干扰。

3.两个线程访问的是synchronized的静态方法

答案:可以实现串行执行。因为synchronized加在静态方法上属于类锁,是同一个对象锁。

4.同时访问同步方法和非同步方法

答案:不可以实现串行执行。非同步方法不受影响。synchronized只作用的修饰的方法上。

5.访问同一个对象的不同的普通同步方法

答案:可以实现串行。因为同一个对象,默认使用的都是this对象,为同一把锁。

6、同时访问静态synchronized和非静态synchronized方案

答案:不可以实现串行。因为静态的锁对象为类锁,非静态的为实例的对象锁,2个锁对象不同,互不影响。

7.方法抛出异常后会释放锁吗?

答案:可以的。synchronized可以在抛异常后自动被释放。lock是需要手动释放的。

性质

可重入

指的是同一个线程的外层函数获得锁之后,内层函数可以直接再次获取该锁

好处:避免死锁,提升封装性

粒度: 

     1.同一个方法是可以重入的

     2.可重入不要求是同一个方法

     3.可重入不要求是同一个类

 

不可中断

sysnchronized一旦这个锁已经被别人获得了,如果我还想获得,我只能等待或者阻塞,直到别的线程释放这个锁。如果别人永远不释放锁,那么我只能永远的等下去。

相比之下: lock类可以拥有中断能力,第一点,如果我觉得我等的时间太长了,有权中断现在已经获取到锁的线程的执行。第二点,如果我觉得我等待的时间太长了不想再等了,也可以退出。

原理

查看monitor指令

通过反编译字节码查看原理

.java文件

java高并发之魂:Synchronize

反编译:

java高并发之魂:Synchronize

java高并发之魂:Synchronize

可重入原理(加锁次数计数器)

1.jvm 负责跟踪对象被加锁的次数

2.线程第一次给对象加锁的时候,计数变为1,每当这个相同的线程在此对象上再次获得锁时,计数会递增

3.每当任务离开时,计数递减,当技术为0时候,锁被完全释放

可见性原理

java高并发之魂:Synchronize

当被sysnchronized修饰之后的方法,在退出前会把数据刷新的主内存,当下个方法进入时候会从主存中拿到最新的数据

缺陷

效率低:锁对象释放的情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程(lock可以)

不够灵活:加锁和释放的时机单一,每个锁仅仅有单一的条件(某个对象),可能是不够的(lock可以)

无法知道是否成功的获取锁

使用注意点:

锁对象不能为空,作用域不宜过大,避免死锁

如何选择lock和synchronized关键字(避免出错为原则)

1.如果可以都不用,而是用java.util.current包中的工具类(countdownluch。。。等)

2.如果适用,就优先使用synchronized

3.只有有必要使用lock的特性的时候才使用lock