JUC并发编程-原理篇

java内存模型

在线程并发编程的问题中,通常存在两个关键的问题:
1.线程间如何通信:线程之间以何种机制交换信息
2.线程间如何同步:线程间以何种机制控制线程间操作发生的相对顺序
针对上面两个问题,有两种解决并发模型解决方法:
1.消息传递并发模型
2.共享内存并发模型

既然堆是共享的,为什么在堆中会有内存不可见问题?
这是因为现代计算机为了高效,往往会在高速缓存区中缓存共享变量,因为cpu访问缓存区比访问内存要快得多。而线程并不是直接操作在内存里的变量,而是共享变量会在每个线程中存一个副本,会导致不可见的现象。根据JMM的规定,线程对共享变量的所有操作都必须在自己的本地内存中进行,不能直接从主内存中读取。那么怎么知道这个共享变量的被其他线程更新了呢?这就是JMM的功劳了,也是JMM存在的必要性之一。JMM通过控制主内存与每个线程的本地内存之间的交互,来提供内存可见性保证。Java中的volatile关键字可以保证多线程操作共享变量的可见性以及禁止指令重排序,synchronized关键字不仅保证可见性,同时也保证了原子性(互斥性)。在更底层,JMM通过内存屏障来实现内存的可见性以及禁止重排序。为了程序员的方便理解,提出了happens-before,它更加的简单易懂,从而避免了程序员为了理解内存可见性而去学习复杂的重排序规则以及这些规则的具体实现方法。

重排序与happens-before

计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排。

为什么指令重排序可以提高性能?

简单地说,每一个指令都会包含多个步骤,每个步骤可能使用不同的硬件。因此,流水线技术产生了,它的原理是指令1还没有执行完,就可以开始执行指令2,而不用等到指令1执行结束之后再执行指令2,这样就大大提高了效率。
指令重排一般分为以下三种:

编译器优化重排

编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。

指令并行重排

现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性(即后一个执行的语句无需依赖前面执行的语句的结果),处理器可以改变语句对应的机器指令的执行顺序。

内存系统重排

由于处理器使用缓存和读写缓存冲区,这使得加载(load)和存储(store)操作看上去可能是在乱序执行,因为三级缓存的存在,导致内存与缓存的数据同步存在时间差。
可适当使用 final、volatile、synchronize来保证多线程下的同步

volatile的内存语义

在Java中,volatile关键字有特殊的内存语义。volatile主要有以下两个功能:

保证变量的内存可见性
禁止volatile变量与普通变量重排序

synchronized与锁`

// 关键字在实例方法上,锁为当前实例
public synchronized void instanceLock() {
    // code
}

// 关键字在静态方法上,锁为当前Class对象
public static synchronized void classLock() {
    // code
}

// 关键字在代码块上,锁为括号里面的对象
public void blockLock() {
    Object o = new Object();
    synchronized (o) {
        // code
    }
}

CAS与原子操作

CAS的全称是:比较并交换(Compare And Swap)。在CAS中,有这样三个值:

V:要更新的变量(var)
E:预期值(expected)
N:新值(new)
比较并交换的过程如下:

判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。

CAS实现原子操作的三大问题
1 ABA问题
所谓ABA问题,就是一个值原来是A,变成了B,又变回了A。这个时候使用CAS是检查不出变化的,但实际上却被更新了两次。

ABA问题的解决思路是在变量前面追加上版本号或者时间戳。从JDK 1.5开始,JDK的atomic包里提供了一个类AtomicStampedReference类来解决ABA问题。

2 循环时间长开销大
3 只能保证一个共享变量的原子操作
使用JDK 1.5开始就提供的AtomicReference类保证对象之间的原子性,把多个变量放到一个对象里面进行CAS操作;
使用锁。锁内的临界区代码可以保证只有当前线程能操作

AQS

AQS是AbstractQueuedSynchronizer的简称,即抽象队列同步器,从字面意思上理解:

抽象:抽象类,只实现一些主要逻辑,有些方法由子类实现;
队列:使用先进先出(FIFO)队列存储数据;
同步:实现了同步的功能。
AQS内部使用了一个volatile的变量state来作为资源的标识。
getState()
setState()
compareAndSetState()

AQS的设计是基于模板方法模式的,它有一些方法必须要子类去实现的,它们主要有:

isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。

tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。

tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。

tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值