Java并发的底层实现原理(part 1)

术语 英文单词 属于描述
内存屏障 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");
    }
}
  • 当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
  • 代码块同步是使用 monitorentermonitorexit 指令实现的,方法的同步同样可以使用这两个指令来实现。
  • 对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

next:对象头和锁升级与对比

发布了11 篇原创文章 · 获赞 0 · 访问量 59

猜你喜欢

转载自blog.csdn.net/weixin_44078008/article/details/105696242