互斥锁。退出方式:1.正常退出。2.异常

2.3.3.1 Synchronized原理

1)修饰普通方法,锁在当前实例对象

2)修饰静态方法,锁在当类的class对象

3)同步代码块,锁是括号里的对象,除基本类型外每个实例都有一个对象锁,比如synchronized(object),需要拿到object的对象锁才能执行。

如下代码

:class RetryTest1 {  private int i = 0;  private static int s = 0;  public void add(){ //线程不安全    s++;  }  public void addSyncI(){ //线程安全    synchronized (this) {      i++;    }  }  public void addSyncS(){ //线程不安全,s是静态变量,当多线程调用不同类对象时,不安全    synchronized (this) {      s++;    }  }  public synchronized void addSyncFun(){ //线程不安全    s++;  }  public static synchronized void addSyncStaticFun(){ //线程安全    s++;  }}

原理:

1.同步方法和同步代码块底层都是通过monitor来实现同步的。

2.两者的区别:同步方式是通过方法中的access_flags中设置ACC_SYNCHRONIZED标志来实现;同步代码块是通过monitorenter和monitorexit来实现

3.我们知道了每个对象都与一个monitor相关联。而monitor可以被线程拥有或释放。

也就是说,线程是通过Monitor管理的,那对象如何与Monitor关联呢?

对象存储在堆内存,包括对象头、实例数据、填充数据。对象头存储Mark Word和指向元数据的指针。为了节省空间,Mark Word,JVM采用空间复用,不同时期存储不同,无锁态时:存储的是hashCode、分代年龄。重量级锁时:存储的是指向monitor的指针。synchronized是重量级锁。

monitor(又称管程),在Java中是ObjectMonitor(JVM源码中C++实现)来实现管程。

想获取锁,也就是遇到Synchronized,进入_EntryList阻塞队列,获取到锁,进入_owner,调用wait,进入_WaitSet。当notify时,从_WaitSet到_EntryList。

引入管程的原因

信号量机制的缺点:进程自备同步操作,P(S)和V(S)操作大量分散在各个进程中,不易管理,易发生死锁。

管程特点:管程封装了同步操作,对进程隐蔽了同步细节,简化了同步功能的调用界面。

2.3.3.2 jdk对Synchronized优化

1.自旋锁

线程频繁阻塞和挂起浪费CPU资源,在挂起之前,执行一段无意义的循环,是时间限制内,如果获取到锁,就很有用,如果获取不到再挂起,但是此时循环是无意义的。

自旋锁在JDK 1.4.2中引入,默认关闭,但是可以使用-XX:+UseSpinning开开启,在JDK1.6中默认开启。同时自旋的默认次数为10次,可以通过参数-XX:PreBlockSpin来调整;

2.自适应自旋锁

JDK 1.6引入,所谓适应就是如果如果线程自旋成功了,那么下次就再次自旋的次数就增加,虚拟机任务上次就成功了,这次也能够成功。反之减少次数,减少资源浪费。

3.锁消除

虚拟机依据是逃逸分析的数据支持,对不可能存在共享资源竞争时,对锁消除。比如使用JDK提供的加锁的api时。

4.锁粗化

一般优化是同步范围尽可能小,才能很快处理完毕,别的线程才能尽快拿到锁。但是如果频繁的遇到加锁解锁过程,会浪费性能,虚拟机会合并更大的加锁解锁。

5.轻量锁

对于大部分锁,整个生命周期都不存在竞争。过程如下:

A.   加锁:当前线程执行CAS操作。如果成功则继续执行同步代码,失败说明有其他线程占有。如果有两个线程争用同一个锁,则轻量锁不再生效,膨胀为重量级锁。

B.   解锁:也是通过使用CAS操作来完成。

注意:

A.   可以膨胀为重量级锁。

B.   高效是因为使用CAS。

C.   在有互斥锁竞争下,效率低,因为除了互斥量还有CAS操作。

6.偏向锁

总是偏向第一个获得锁的线程,如果接下来一直没有其他线程竞争,第一线程永远不需要同步操作。比轻量锁效率更高,因为CAS都省略了。但是如果有其他线程来竞争,则偏向锁是多余的,可以膨胀为轻量锁。

总结

A.   自旋锁、锁清除、锁粗化都是默认开启的,他们的存在会提高效率。

B.   偏向锁和轻量锁是在特殊的场景下,才可以提高性能。否则还会降低性能。

C.   偏向锁可以膨胀为轻量锁,轻量锁可以膨胀为重量级锁。不可反向膨胀

2.3.3.3 Lock:

区别:

Synchronized的缺陷:当一个线程获取锁,其他线程只能等待,会影响效率。类别synchronizedLock存在层次Java的关键字,在jvm层面上是一个类

锁的释放1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁在finally中必须释放锁,不然容易造成线程死锁

锁的获取假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待

锁状态无法判断可以判断

锁类型可重入 不可中断 非公平可重入 可判断 可公平(两者皆可)

性能少量同步大量同步