Java之Synchronized

来源:互联网 发布:c语言成绩管理系统6.0 编辑:程序博客网 时间:2024/06/06 17:43

一、CAS

CAS(compare and swap),解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。 具体来说,就是当一个CPU要操作某一块内存区域的时候,会记录下操作前,内存的值A,放在Cache中,当CPU操作完成完成后,把A的值修改为新的值B,然后需要把新的值写回内存,这时就会比较,现在内存的值是否和取的时候的值A一样,如果一样就认为该内存没有被修改,则用B覆盖掉A。
很显然,CAS只适用于多CPU的处理器。CAS是靠硬件实现的,JVM只是封装了汇编调用。CAS会导致“ABA”问题,也就是写回的时候,虽然于预期原值(A)相同,但是有可能是先被修改成A‘,然后又被修改为A,这时虽然与预期值一样,虽然满足CAS的条件,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。

、Synchronized

在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了

Java中的每一个对象都可以作为锁。

  • 对于同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前对象的Class对象。
  • 对于同步方法块,锁是Synchonized括号里配置的对象。

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

2.1 锁存在的位置

锁存在Java对象头里。如果对象是数组类型,则虚拟机用3个Word(字宽)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,一字宽等于四字节,即32bit。

长度内容说明32/64bitMark Word存储对象的hashCode或锁信息等。32/64bitClass Metadata Address存储到对象类型数据的指针32/64bitArray length数组的长度(如果当前对象是数组)

Java对象头里的Mark Word里默认存储对象的HashCode,分代年龄和锁标记位。32位JVM的Mark Word的默认存储结构如下:

 25 bit4bit1bit是否是偏向锁2bit锁标志位无锁状态对象的hashCode对象分代年龄001

在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。Mark Word可能变化为存储以下4种数据:

锁状态

25 bit

4bit

1bit2bit23bit2bit是否是偏向锁锁标志位轻量级锁指向栈中锁记录的指针00重量级锁指向互斥量(重量级锁)的指针10GC标记空11偏向锁线程IDEpoch对象分代年龄101

多个线程访问同一个临界区域的时候,只有一个线程能进入(悲观锁),其他的线程就会处于阻塞,阻塞操作由操作系统完成(在Linxu下通过pthread_mutex_lock函数)。线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能。java利用CAS和多种状态的锁来缓解这个问题。锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。

2.2 偏向锁
偏向锁主要解决无竞争下的锁性能问题,大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要花费CAS操作来加锁和解锁,而只需简单的测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。简单来说,某个加锁对象只记得最近一次获得它的线程ID,如果下一次访问的还是同一个线程ID,就给他一个绿色通道,不必再走各种加锁/解锁流程。如果不是,该走什么流程你还得走(这时也同时触发了偏向锁的撤销,偏向锁使用了一种等到竞争出现才释放锁的机制)
2.3轻量级锁
若获得锁的线程能在很短的时间内释放锁,则那些正在争用线程可以稍微等一等(自旋),在线程释放锁后,争用线程可能会立即得到锁,从而避免了系统阻塞。简单来说,就是当我要获得锁的时候,发现已经被其他线程占用,那么我就先等一等吧,如果能一定时间内,释放锁了,那么就可以立即获得锁。虽然不是很公平,但是减少了响应时间
2.4重量级锁
如果在自旋时间内,没有取得锁,那么就会变为重量级锁,当锁处于这个状态下,其他线程试图获取锁时,都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮的夺锁之争


0 0