知识储备
主内存与工作内存
Java内存模型的成员变量都存储在主内存(Main Memory)(也就是堆和方法区内存)中,此外每条线程都有自己的工作内存(Working Memory)(也就是栈内存)。
线程对成员变量操作: 从主内存中拷贝变量到自己的工作内存 ①> 对变量进行操作后 ②> 将操作后的值写入主内存。
volatile
特点:
- 可见性:volatile 保证了变量每次修改都能立即同步到主内存,每次使用前立即从主内存刷新。保证每个线程每次使用拿到的都是最新值,而普通变量做不到这点。
- 有序性 (通过禁止指令重排序优化):volatile 通过设置 Java 内存屏障禁止重排序优化。(这会让volatile变量在写的过程中插入内存屏障指令,从而付出性能代价)
与普通变量区别:
- volatile每次读都必须去主内存中Load①
- volatile每次写都必须立即写到主内存Store②
- 线程对普通变量只会读一次
volatile变量读写并不是一个原子操作,volatile不具备原子性
完全安全的只有:synchronized 和 java.util.concurrent.atomic 中的原子类
CAS算法
CAS全称是Compare and Swap,即比较并交换,是通过原子指令来实现多线程的同步功能,将获取存储在内存地址的原值和现值进行比较,只有当他们相等时,交换指定的预期值和内存中的值,这个操作是原子操作,若不相等,则返回false。
CAS是一种无锁算法,有3个关键操作数,内存地址,旧的内存中预期值,要更新的新值,当内存值和旧的内存中预期值相等时,将内存中的值更新为新值。
乐观锁与悲观锁
-
CAS属于乐观锁(Optimistic Locking),乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就利用循环重试,直到成功为止。
-
synchronized是悲观锁(Pressimistic Locking),被一个线程拿到锁之后,其他线程必须等待该线程释放锁,性能较差。
java.util.concurrent.atomic 中的原子类
Unsafe类是CAS实现的核心,方法由C++编写实现原子操作。
而java.util.concurrent.atomic 中的原子类就是Unsafe类的各种包装类。
以AtomicInteger为例重要方法
绝对的线程安全
- 自增1后返回值:count.incrementAndGet();
- 自减1后返回值:count.decrementAndGet();
- 返回值后自减1:count.getAndDecrement();
- 返回值后自加1:count.getAndIncrement();
- 返回值后加上delta:count.getAndAdd(int delta);
- boolean compareAndSet(int expect, int update) : 如果当前值等于预期值(expect),则以原子方式将该值设置为输入值(update)【这个方法叫CAS操作】
结合for循环的重要实现方式
原子更改对象中的volatile属性:Atomic…FieldUpdater
原子读写一个对象:AtomicReference
CAS的ABA问题
解决办法:AtomicStampedReference 与 AtomicMarkableReference
但是:AtomicMarkableReference并不能很好的避免问题,因为它每次设置的是一个标志,而这个标志是boolean类型
AtomicStampedReference:
AtomicStampedReference原子类是一个带有时间戳的对象引用,在每次修改后,AtomicStampedReference不仅会设置新值而且还会记录更改的时间。
只是增加了一个时间戳对象,用法和临界变量一模一样!
//初始化时需要传入一个初始值和初始时间
private AtomicStampedReference tail = new AtomicStampedReference(0,0);
public boolean enqueue(Object item){
int preTail = 0;
int newTail = 0;
int preTime = 0;
int newTime = 0;
for (;;){
preTail = (Integer)tail.getReference();//获取此刻的变量值
preTime = tail.getStamp();//获取获取此刻变量的时间值
//修改获得值
newTail = (preTail+1)%capacity;
newTime = preTime + 1;
if (newTail == head.get()) return false;
if (tail.compareAndSet(preTail,newTail,preTime,newTime)){//验证变量值、变量时间是否为有被更改
items[preTail] = item;
return true;
}
}
}
实战
注意点:
- 往往需要被操作的数据集合由 volatile 修饰保证可见性
- 而逐增、逐减的关键变量需要的是 CAS 原子操作
- 要在for循环中获得、操作、判断和存储,成功则直接return就可以。失败的话,循环再次获得…
具体实现模型:data01/src/data/queue
package data.queue;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 数组循环队列
*/
public class LoopArrayQueue {
private volatile Object[] items;
private AtomicInteger head;
private AtomicInteger tail;
private int capacity;
public LoopArrayQueue(int capacity) {
this.capacity = capacity;
this.items = new Object[capacity];
this.head = new AtomicInteger(0);
this.tail = new AtomicInteger(0);
}
public boolean enqueue(Object item){
int preTail = 0;
int newTail = 0;
for (;;){
preTail = tail.get();//①获得此刻的tail值
newTail = (preTail+1)%capacity;
if (newTail == head.get()) return false;
if (tail.compareAndSet(preTail,newTail)){//验证此刻主内存的tail值是否为①出所获得的值 如果是将新值赋予tail
items[preTail] = item;
return true;
}
}
}
public Object dequeue(){
int preHead = 0;
int newHead = 0;
for (;;){
preHead = head.get();
if (preHead == tail.get()) return null;
newHead = (preHead + 1)%capacity;
if (head.compareAndSet(preHead,newHead)){
return items[preHead];
}
}
}
}
联想
与之相对的synchronized与wait和notify结合使用,且wait在while循环中!