Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

008-week 02 #38

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions week_02/08/AtomicInteger-008.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# 读源码--AtomicInteger

## 属性

unsafe属性:为了保证线程安全,AtomicIngeger的增减方法的底层都用到了Unsafe类的cas方法

```java
private static final Unsafe unsafe = Unsafe.getUnsafe();
```

valueOffset属性:字段的偏移地址,在cas中用来得到当前字段的值

```java
private static final long valueOffset;
```

value属性:此处用volatile修饰,volatile,被称为“轻量级的synchronized”,但只能修饰变量。

```java
private volatile int value;
```

## 方法

AtomicInteger的实现方法模式基本一致,用一个方法说明,getAndSet方法

```java
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
// get在前,返回值则为修改前的值;若get在后,则返回值为修改后的值
public final int getAndSetInt(Object var1, long var2, int var4) {
int var5;
// 此处用到cas算法,保证线程安全
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var4));

return var5;
}
```

## CAS

### CAS概念

比较并交换,unsafe中有该方法,广泛应用与文字操作。CAS是乐观锁技术,当多个线程尝试更新同一个变量时,只有一个能更新成功,而其他线程都失败。失败的线程不会被挂起,而是被告知失败,重新尝试。

### CAS 缺点

1.循环时间长开销大

2.只能保证一个共享变量的原子操作

3.ABA问题。解决办法,版本控制。Java中提供了AtomicStampedReference通过控制变量值的版本来保证CAS的正确性。

## volatile

### 特性

1.保证了不同线程对变量操作的内存可见性(可见性);

2.禁止指令重排序(有序性)

3.无法保证复合操作的原子性。要想保证原子性,只能借助于synchronized,Lock及原子操作了,如Atomic系列类

https://juejin.im/post/5a2b53b7f265da432a7b821c volatile面试强文

### 并发编程三大特性

1.原子性:Java中对基本数据类型的读取和赋值操作

2.可见性:Java利用volatile来提供可见性。变量被volatile修饰是,对它的修改会立即刷新到贮存;当其他线程需要读取该变量是,会从主存中共读取新值。

3.有序性:JMM允许编译器和处理器对指令重排序。对单线程没有影响,对多线程有影响/

```java
int a = 0;
bool flag = false;

public void write() {
a = 2; //1
flag = true; //2
}

public void multiply() {
if (flag) { //3
int ret = a * a;//4
}

}
// 若线程1先执行write方法中flag=true;线程2执行multiply,线程1再执行a=2;
```

### 应用场景

1.状态量标记:标记为volatile可以保证修改对线程立即可见。比synchronized,Lock有一定的效率提升。

2.单例模式的实现

```java
class Singleton{
// 可避免初始化操作的指令重排序
private volatile static Singleton instance = null;

private Singleton() {

}

public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
```

Binary file added week_02/08/AtomicInteger-008.xmind
Binary file not shown.
263 changes: 263 additions & 0 deletions week_02/08/AtomicStampedReference-008.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
# 读源码--AtomicStampedReference

## ABA问题

在CAS过程中,会出现ABA问题

```java
public class ABATest {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(1);
new Thread(() -> {
int value = atomicInteger.get();
System.out.println("Thread 1 read value:"+value);

LockSupport.parkNanos(100000000L);
if (atomicInteger.compareAndSet(value, 3)) {
System.out.println("Thread 1 update from " + value + " to 3");
} else {
System.out.println("Thread 1 update fail !");
}
}).start();

new Thread(() -> {
int value = atomicInteger.get();
System.out.println("Thread 2 read value:" + value);

if (atomicInteger.compareAndSet(value, 2)) {
System.out.println("Thread 2 update from "+value+" to 2");
}

value = atomicInteger.get();
System.out.println("Thread 2 read value:"+value);
if (atomicInteger.compareAndSet(value, 1)) {
System.out.println("Thread 2 update from "+value+" to 3");
}
}).start();
}
}
```

结果

```java
Thread 1 read value:1
Thread 2 read value:1
Thread 2 update from 1 to 2
Thread 2 read value:2
Thread 2 update from 2 to 3
Thread 1 update from 1 to 3
```



## ABA危害

例子,一个无锁的栈结构

```java
public class ABATest1 {
static class Stack{
private AtomicReference<Node> top = new AtomicReference();

static class Node {
int value;
Node next;
public Node(int value) {
this.value = value;
}
}

// 出栈
public Node pop() {
for (; ; ) {
// 获取栈顶节点
Node t = top.get();
if (t == null) {
return null;
}

Node next = t.next;
// top指向栈顶的next
if (top.compareAndSet(t, next)) {
t.next = null; // next清空防止外面直接操作栈
return t;
}
}
}

public void push(Node node) {
for (; ; ) {
// 获取栈顶节点
Node t = top.get();

node.next = t;
// 更新top指向node节点
if (top.compareAndSet(t, node)) {
return;
}

}
}
}

public static void testStack() {
// 初始化栈top->1->2->3
Stack stack = new Stack();
stack.push(new Stack.Node(3));
stack.push(new Stack.Node(2));
stack.push(new Stack.Node(1));

new Thread(() ->{
// 线程1出栈一个元素
stack.pop();
}).start();

new Thread(() ->{
// 线程2出栈两个元素
Stack.Node A = stack.pop();
Stack.Node B = stack.pop();
// 线程2入栈A元素
stack.push(A);
}).start();

}

public static void main(String[] args) {
testStack();
}
}
```

初始化栈top->1->2->3

1).线程1出栈,在top.compareAndSet(t, next)暂停,未弹出节点1

2).线程2,出栈,弹出节点1,栈结构变为top->2->3

3).线程2,出栈,弹出节点2,栈结构变为top->3

4).线程2,入栈,添加节点1,栈结构变为top->1->3

5).线程1,弹出节点1,结果top->2

竟然不是top->3?

因为线程1在第一步保持的next时节点2,所以它执行成功后top节点就指向节点2了

## ABA解决方法

1.版本号

2.不重复使用节点的引用。如上述节点1

3.直接操作元素而不是节点。

## AtomicStampedReference源码

### 内部类

```java
private static class Pair<T> {
final T reference; //值
final int stamp; //版本号
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
```

### 属性

```java
private volatile Pair<V> pair;
```

### 构造器

```java
/**
* Creates a new {@code AtomicStampedReference} with the given
* initial values.
*
* @param initialRef the initial reference 初始引用
* @param initialStamp the initial stamp 初始版本号
*/
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
```

### 改造ABA问题例子

```java
public class ABATest2 {
public static void main(String[] args) {
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);
new Thread(() -> {
int[] stampHolder = new int[1];
int value = atomicStampedReference.get(stampHolder);
int stamp = stampHolder[0];
System.out.println("Thread 1 read value:"+value);
System.out.println("Thread 1 read stamp:"+stamp);

LockSupport.parkNanos(100000000L);

value = atomicStampedReference.get(stampHolder);
stamp = stampHolder[0];
System.out.println("After parking,Thread 1 read value:"+value);
System.out.println("Thread 1 read stamp:"+stamp); // 版本号变为3了
if (atomicStampedReference.compareAndSet(value,3,stamp,stamp+1)) {
System.out.println("Thread 1 update from " + value + " to 3");
} else {
System.out.println("Thread 1 update fail !");
}
}).start();

new Thread(() -> {
int[] stampHolder = new int[1];
int value = atomicStampedReference.get(stampHolder);
int stamp = stampHolder[0];
System.out.println("Thread 2 read value:" + value);
System.out.println("Thread 2 read stamp:"+stamp);

if (atomicStampedReference.compareAndSet(value,2,stamp,stamp+1)) {
System.out.println("Thread 2 update from "+value+" to 2");
}

value = atomicStampedReference.get(stampHolder);
stamp = stampHolder[0];
System.out.println("Thread 2 read value:"+value);
System.out.println("Thread 2 read stamp:"+stamp);
if (atomicStampedReference.compareAndSet(value,3,stamp,stamp+1)) {
System.out.println("Thread 2 update from "+value+" to 3");
}
}).start();
}
}
```

结果:

```java
Thread 1 read value:1
Thread 1 read stamp:1
Thread 2 read value:1
Thread 2 read stamp:1
Thread 2 update from 1 to 2
Thread 2 read value:2
Thread 2 read stamp:2
Thread 2 update from 2 to 3
After parking,Thread 1 read value:3
Thread 1 read stamp:3
Thread 1 update from 3 to 3
```





Binary file added week_02/08/AtomicStampedReference-008.xmind
Binary file not shown.
Loading