Java中 synchronized 关键字的理解

synchronized 关键字的理解

在Java中,synchronized 是一个重量级的控制并发的关键字。

这个关键字可以保证并发过程所必须的“原子性”,“可见性”,“有序性”

一,用法:

在方法内加锁:

synchronized 块在代码编译后生成的字节码中实际上是加上monitorenter 和monitorexit。

//对当前的this对象加锁,和synchronized成员方法是一样的,和synchronized成员方法会互斥,同一时间只能有一个进入。
synchronized (this) {
		
}
//对TestUtil类对象加锁,和静态synchronized方法是一样的,和synchronized静态方法会互斥,同一时间只能有一个进入。
synchronized (TestUtil.class) {
	
}
//对infoVO对象加锁,对某个对象加锁,如果有其他操作这个对象的锁方法,会互斥。
synchronized (infoVO){

}

在方法上加锁:

synchronized 方法则是在方法的【flags:】标志上多一个 ACC_SYNCHRONIZED

//对该类的静态方法加锁,多个线程调用同一个类的synchronized静态方法时同一时间只能有一个进入(如果有多个静态方法都是synchronized,也是同一时间只能有一个进入,因为锁针对的是类)。
public synchronized static void staticMethod() {
}

//对当前对象的这个成员方法加锁,多个线程调用同一个对象的synchronized成员方法时同一时间只能有一个进入。(如果有多个成员方法都是synchronized,也是同一时间只能有一个进入,因为锁针对的是对象)
public synchronized void simpleMethod() {
}

二,分类

从上面可以看出,synchronized 的加锁分为两类

  • 对对象的锁
  • 对类的锁

三,锁的优化

synchronized锁是JAVA内置的,jdk对其一直持续优化。

对象头的结构

Java中 synchronized 关键字的理解_第1张图片

  • 自旋锁

因为java线程映射到内核进程,频繁的线程挂起唤醒对性能造成的影响非常大,所以在jdk1.5之后加入了自旋锁,在等待时间稍短的时候会进行自旋等待,但是自旋会造成cpu的损耗,如果长时间持续自旋仍然拿不到锁,其实对性能反而是种损耗。

使用-XX:+UseSpining开启。自旋次数使用-XX:+PreBlockSpin来更改。

  • 轻量级锁

轻量级锁是当请求时,当一个线程来请求锁时,如果同步对象没有被锁定,虚拟机首先将在当前线程的栈帧中建立一个名叫锁记录的空间,用于存储锁对象目前Mark Word的拷贝,然后将对象的Mark Word 更新为这个指针。

如果成功,则获取到了锁,并且对象的MARK Word的锁标志转变为00,变为轻量级锁。如果失败,则判断对象的Mark Word是否已经指向了当前线程的栈帧,如果有则说明已经获取到了锁,正常执行,如果没有,则说明其他线程获取到了这个锁。

如果有两个以上的线程用同一个锁,则轻量级锁会失效,直接膨胀变成重量级锁。标志位变为10

使用了CAS交换Mark Word和栈帧来加锁和解锁

  • 偏向锁

偏向锁的偏就是偏心的偏,偏向锁会偏向于第一个获得锁的线程,当第一个线程获取到了锁之后,虚拟机把对象头标志位设为01,偏向模式。用cas操作把线程ID记录在Mark Word中,如果CAS成功,则后续持有偏向锁的这个线程进入时不再需要做任何操作。

而当有另一个线程去尝试获取锁时,则偏向模式结束,撤销偏向恢复到未锁定01或者轻量级锁00。

使用-XX:+UseBisedLocking 来启用。

  • 偏向锁和轻量级锁区别

轻量级锁是认为没有竞争,每个线程来的时候不需要阻塞,直接可以通过CAS操作来进行对象锁的更改;而偏向锁则认为锁不会被不同的线程获取(永远只被一个线程使用),所以更暴力,直接连CAS都不做,是这个线程则直接执行,不是这个线程则锁膨胀。

  • 重量级锁

在上面的锁的优化都无法解决时,锁会膨胀成重量级锁,重量级锁通过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态到内核态的切换,切换成本非常高。

你可能感兴趣的:(java基础知识,多线程)