锁的内存含义

1.锁释放和获取建立的happens-before关系:

锁是Java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的

线程向获取同一个锁的线程发送消息。 下面是锁释放-获取的示例代码。

class MonitorExample { int a = 0;

    public synchronized void writer() { // 1 
    a++; // 2

} // 3
 public synchronized void reader() {// 4

    int i = a; // 5
 }



} // 6 }

假设线程A执行writer()方法,随后线程B执行reader()方法。根据happens-before规则,这个 过程包含的happens-before关系可以分为3类。

1)根据程序次序规则,1 happens-before 2,2 happens-before 3;4 happens-before 5,5 happens- before 6。

2)根据监视器锁规则,3 happens-before 4。

3)根据happens-before的传递性,2 happens-before 5。 上述happens-before关系的图形化表现形式如图3-24所示。

2、锁获取和释放的内存含义:

释放:就是JMM会把本地修改的数据刷新到主内存中

获取:当获取到锁以后发现本地对应的值已经修改,那么从主内存中获取最新的值

下面对锁释放和锁获取的内存语义做个总结。

·线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A 对共享变量所做修改的)消息。

·线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共 享变量所做修改的)消息。

·线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发 送消息。

3、锁内存语义的实现:

ReentrantLock的实现依赖于Java同步器框架AbstractQueuedSynchronizer(本文简称之为 AQS)。AQS使用一个整型的volatile变量(命名为state)来维护同步状态,马上我们会看到,这 个volatile变量是ReentrantLock内存语义实现的关键。

 

公平锁:加锁流程

1.ReentrantLock lock();

2.FairSync lock();

3.aqs acquire()

4.FairSync tryAcquire()这个里面进行比较和;

公平锁和非公平锁的区别是

1、非公平锁会CAS一边如果为0直接修改,不为0进acqurie和tryacquire

2、公平锁要调用

hasQueuedPredecessors看是否是他的node
//公平锁 
protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
//非公平锁
  final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //读取volatile修饰的state值插入内存屏障
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

编译器不会对volitile写和volatile写前面任意内存操作重排序。意味着为了同 时实现volatile读和volatile写的内存语义,编译器不能对CAS与CAS前面和后面的任意内存操 作重排序。

intel的手册对lock前缀的说明如下。

1)确保对内存的读-改-写操作原子执行。在Pentium及Pentium之前的处理器中,带有lock前 缀的指令在执行期间会锁住总线,使得其他处理器暂时无法通过总线访问内存。很显然,这会 带来昂贵的开销。从Pentium 4、Intel Xeon及P6处理器开始,Intel使用缓存锁定(Cache Locking) 来保证指令执行的原子性。缓存锁定将大大降低lock前缀指令的执行开销。

2)禁止该指令,与之前和之后的读和写指令重排序。

3)把写缓冲区中的所有数据刷新到内存中。

现在对公平锁和非公平锁的内存语义做个总结:

·公平锁和非公平锁释放时,最后都要写一个volatile变量state。

·公平锁获取时,首先会去读volatile变量。

·非公平锁获取时,首先会用CAS更新volatile变量,这个操作同时具有volatile读和volatile 写的内存语义。

concurrent包实现:

如果我们仔细分析concurrent包的源代码实现,会发现一个通用化的实现模式。

首先,声明共享变量为volatile。

然后,使用CAS的原子条件更新来实现线程之间的同步。

同时,配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的 通信。

AQS,非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),这些concurrent 包中的基础类都是使用这种模式来实现的,而concurrent包中的高层类又是依赖于这些基础类 来实现的。从整体来看,concurrent包的实现示意图如3-28所示。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值