多线程—Synchronized关键字

最近学了多线程的锁机制,记录一下,以便日后复习。

一、引言

多线程中经常会出现多个线程访问同一共享资源的情况(多线程共享资源可以节省系统开销、提高效率),但也会导致数据访问冲突。如何实现线程间的有机交互,并确保共享资源在某时只能被一个线程访问,就是线程同步。线程锁则是实现线程同步的方法。

二、synchronized

所谓的多线程的锁,就是能够防止多线程同时访问同一共享资源,保障同一时间只有一个线程访问共享资源。

2.1 synchronized

synchronized解决的是多线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或代码块在任意时刻只能有一个线程执行。即当一个线程访问一个被synchronized修饰的代码块时,会自动获取对应的一个锁,并在执行改代码块时,其他线程想要访问这个代码块,会处于等待状态,知道当前线程释放锁后,其他线程才能进行资源的竞争,竞争获取到的线程才能访问这个代码块。

2.1.1 synchronized关键字的使用方式
1)修饰实例方法

作用:作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁

synchronized void method(){
}
2) 修饰静态方法

作用:作用于当前类,即给类的所有实例对象加锁。

synchronized void static method(){
}
3)修饰代码块

作用:根据指定的对象,给其加锁。如synchronized(this.object)则是给对象加锁,synchronized(类.class)则是给类加锁

//给对象加锁
synchronized(this){
}

//给类加锁
public static class Test{
synchronized(Test.class){
}

注:
1、在定义接口方法时不能使用synchronized关键字
2、构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步

2.1.2 synchronized原理

摘自:https://www.cnblogs.com/paddix/p/5367116.html
首先,通过反编译查看Synchronized是如何实现对代码块进行同步的

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("Method 1 start");
        }
    }
}

在这里插入图片描述
关于这两条指令的作用,我们直接参考JVM规范中描述:
monitorenter :

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

这段话的大概意思为:
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

monitorexit:

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

这段话的大概意思为:
执行monitorexit的线程必须是objectref所对应的monitor的所有者。
指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
  通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
  我们再来看一下同步方法的反编译结果:
源代码:

package com.paddx.test.concurrent;

public class SynchronizedMethod {
    public synchronized void method() {
        System.out.println("Hello World!");
    }
}

反编译结果:
在这里插入图片描述
从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

总结:

1、Synchronized修饰同步代码块实现使用的是monitorenter和monitorexit指令,其中monitorentry指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置。
当执行monitorenter指令时,线程试图获取锁也就是获取对象监视器monitor的持有权。
在执行monitorenter时,会尝试获取对象锁,如果锁的计数器为0则表示可以锁是可以获取的,获取后将锁计数器加1.
在执行monitorexit指令后,将锁计数器减1,如果此时所计数器为0,则表示锁被释放。
2、
Synchronized修饰方法时,没有monitorenter和monitorexit指令,取而代之的是ACC_SYNCHRONIZED标示符,该标识指明了该方法是一个同步方法

参考:https://www.cnblogs.com/paddix/p/5367116.html
https://snailclimb.gitee.io/javaguide/#/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值