术语 | 英文单词 | 属于描述 |
---|---|---|
内存屏障 | memory barrier | 实现内存操作的顺序限制 |
缓冲行 | cache line | 缓存中可以分配的最小存储单元 |
原子操作 | atomic operations | 不可中断的一个或者一系列操作 |
缓存行填充 | cache line fill | 当处理器识别到内存中读取操作数是可缓存的,处理器读取缓存行到适当的缓存 |
缓存命中 | cache hit | 高速缓存行填充的操作的内存位置仍然为下次处理机访问的地址时,处理器从缓存中读取数据 |
写命中 | write hit | 处理器将操作数写回到一个内存缓存的区域时,他首先会检查这个缓存的内存地址是否在缓存行中,如果存在一个有效的缓存行,则处理器将这个操作数写回到缓存,而不是写回到内存 |
写缺失 | write misses the cache | 一个有效的缓存行被写入到不存在的内存区域 |
1.volatile的原理
volatile 变量修饰的共享变量,在进行写操作是,会有lock前缀。
lock指令会发生:
- 将当前处理器缓存行的数据写回到系统内存。
- LOCK 信号一般不锁总线,而是锁缓存
- 锁定这块内存区域的缓存并回写到内存,并使用缓存一致性机制来确保修改的原子性
- 写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。
2.volatile的优化
LinkedTransferQueue
/** 队列中的头部节点 */
private transient final PaddedAtomicReference<QNode> head;
/** 队列中的尾部节点 */
private transient final PaddedAtomicReference<QNode> tail;
static final class PaddedAtomicReference <T> extends AtomicReference T> {
// 每个对象占用四个字节
Object p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb, pc, pd, pe;
PaddedAtomicReference(T r) {
super(r);
}
}
public class AtomicReference <V> implements java.io.Serializable {
private volatile V value;
// 省略其他代码
}
```
一个对象4字节,上述代码中一共有60个字节的临时变量,加上父类的value变量,一共64个字节。
使用追加到64字节的方式来填满高速缓冲区的缓存行,避免头节点和尾节点加载到同一个缓存行,使头、尾节点在修改时不会互相锁定。
3.synchronized的实现原理
- 对于普通同步方法,锁是当前实例对象。
- 对于静态同步方法,锁是当前类的Class对象。
- 对于同步方法块,锁是Synchonized括号里配置的对象。
public class SynchronizedTest {
public static void main(String[] args) {
Test t = new Test();
Thread a = new Thread(t,"A");
Thread b = new Thread(t,"B");
Thread c = new Thread(t,"C");
a.start();
b.start();
c.start();
}
}
class Test implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("running : "+Thread.currentThread().getName());
if (Thread.currentThread().getName().startsWith("A")) {
asy1();
}else if(Thread.currentThread().getName().startsWith("B")){
asy2();
}else {
asy4();
}
System.out.println("ending : "+ Thread.currentThread().getName());
}
// 同步代码块
public void asy1() {
synchronized(this) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 普通同步方法
public synchronized void asy2() {
System.out.println("this is asy2");
}
// 类同步
public void asy3() {
synchronized(Test.class) {
System.out.println("this is class lock");
}
}
// 静态同步方法
public static synchronized void asy4() {
System.out.println("this is class lock");
}
}
- 当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
- 代码块同步是使用
monitorenter
和monitorexit
指令实现的,方法的同步同样可以使用这两个指令来实现。 - 对象都有一个
monitor
与之关联,当且一个monitor
被持有后,它将处于锁定状态。线程执行到monitorenter
指令时,将会尝试获取对象所对应的monitor
的所有权,即尝试获得对象的锁。
next:对象头和锁升级与对比