6. 共享模式之无锁
6.1 提出问题【引言】
package com.rui.six;
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public static void main(String[] args) {
Account.demo(new AccountUnsafe(10000));
}
}
class AccountUnsafe implements Account{
private Integer balance;
public AccountUnsafe(Integer balance) {
this.balance = balance;
}
@Override
public Integer getBalance() {
return balance;
}
@Override
public void withdraw(Integer amount) {
balance -= amount;
}
}
interface Account {
// 获取余额
Integer getBalance();
// 取款
void withdraw(Integer amount);
/**
* 方法内会启动 1000 个线程,每个线程做取款 10 元的操作
* 如果初始余额为 10000 元,那么终止余额应当是 0 元
*/
static void demo(Account account) {
List<Thread> ts = new ArrayList<>();
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(10);
}));
}
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println("终止余额 = " + account.getBalance()
+ ";cost " + (end - start) / 1000_000 + " ms");
}
}
// 某次运行结果
终止余额 = 180;cost 107 ms
进程已结束,退出代码 0
造成某次运行结果同预期结果不符的原因:非线程安全
6.1.1 解决思路 - 锁
package com.rui.six;
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public static void main(String[] args) {
Account.demo(new AccountUnsafe(10000));
Account.demo(new AccountSynchronized(10000));
}
}
class AccountSynchronized implements Account {
private Integer balance;
public AccountSynchronized(Integer balance) {
this.balance = balance;
}
@Override
public synchronized Integer getBalance() {
return balance;
}
@Override
public synchronized void withdraw(Integer amount) {
balance -= amount;
}
}
class AccountUnsafe implements Account {
private Integer balance;
public AccountUnsafe(Integer balance) {
this.balance = balance;
}
@Override
public Integer getBalance() {
return balance;
}
@Override
public void withdraw(Integer amount) {
balance -= amount;
}
}
interface Account {
// 获取余额
Integer getBalance();
// 取款
void withdraw(Integer amount);
/**
* 方法内会启动 1000 个线程,每个线程做取款 10 元的操作
* 如果初始余额为 10000 元,那么终止余额应当是 0 元
*/
static void demo(Account account) {
List<Thread> ts = new ArrayList<>();
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(10);
}));
}
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println("终止余额 = " + account.getBalance()
+ ";cost " + (end - start) / 1000_000 + " ms");
}
}
// 某次运行结果
终止余额 = 200;cost 120 ms
终止余额 = 0;cost 75 ms
进程已结束,退出代码 0
6.1.2 解决思路 - 无锁
本章内容【问题不大】
package com.rui.six;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Test1 {
public static void main(String[] args) {
Account.demo(new AccountUnsafe(10000));
Account.demo(new AccountSynchronized(10000));
Account.demo(new AccountCas(10000));
}
}
class AccountCas implements Account {
private AtomicInteger balance;
public AccountCas(Integer balance) {
this.balance = new AtomicInteger(balance);
}
@Override
public Integer getBalance() {
return balance.get();
}
@Override
public void withdraw(Integer amount) {
while (true) {
int prev = balance.get();
int next = prev - amount;
if (balance.compareAndSet(prev, next)) {
break;
}
}
}
}
class AccountSynchronized implements Account {
private Integer balance;
public AccountSynchronized(Integer balance) {
this.balance = balance;
}
@Override
public synchronized Integer getBalance() {
return balance;
}
@Override
public synchronized void withdraw(Integer amount) {
balance -= amount;
}
}
class AccountUnsafe implements Account {
private Integer balance;
public AccountUnsafe(Integer balance) {
this.balance = balance;
}
@Override
public Integer getBalance() {
return balance;
}
@Override
public void withdraw(Integer amount) {
balance -= amount;
}
}
interface Account {
// 获取余额
Integer getBalance();
// 取款
void withdraw(Integer amount);
/**
* 方法内会启动 1000 个线程,每个线程做取款 10 元的操作
* 如果初始余额为 10000 元,那么终止余额应当是 0 元
*/
static void demo(Account account) {
List<Thread> ts = new ArrayList<>();
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(10);
}));
}
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println("终止余额 = " + account.getBalance()
+ ";cost " + (end - start) / 1000_000 + " ms");
}
}
// 某次运行结果
终止余额 = 500;cost 135 ms
终止余额 = 0;cost 86 ms
终止余额 = 0;cost 59 ms
进程已结束,退出代码 0
6.2 CAS 和 volatile
while (true) {
int prev = balance.get();
int next = prev - amount;
if (balance.compareAndSet(prev, next)) {
break;
}
}
CAS:compareAndSet,直译 “比较并设置”
//(线程 1 )
int prev = balance.get(); // 获取余额 100
int next = prev - amount; // 减 10 = 90
// (线程 2 )已经修改为 90 了
balance.compareAndSet(prev, next) // 因为100 != 90,所以 return false
// ...
int prev = balance.get(); // 获取余额 80
int next = prev - amount; // 减 10 = 70
balance.compareAndSet(prev, next) // 因为80 == 80,所以将余额修改为 70 并 return true
volatile 关键字对 CAS 中的 C 提供了可见性的支持
【6.1.2 解决思路 - 无锁】的某次运行结果表明,无锁的运行效率高于 synchronized 的运行效率,为什么?
在线程数小于 CPU core 数的条件下, synchronized 会发生上下文切换而无锁不会。
CAS 运用了乐观锁的思想;synchronized 运用了悲观锁的思想
CAS 适用于线程数少、多核 CPU 的情况
线程数少、多核 CPU 不等同于 线程数小于 CPU core数
多线程、单核 CPU 的情况一定会发生上下文切换
线程数多的情况可能会导致 CAS 频繁 return false
import java.util.concurrent.atomic
6.3 原子整数
AtomicBoolean、AtomicInteger、AtomicLong
以 AtomicInteger 为例
构造方法(AtomicInteger)
无参构造
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger();
System.out.println(value.get());
}
}
// 运行结果
0
进程已结束,退出代码 0
有参构造
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger(5);
System.out.println(value.get());
}
}
// 运行代码
5
进程已结束,退出代码 0
incrementAndGet
value.incrementAndGet() 等同于 ++value
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger(5);
System.out.println(value.incrementAndGet());
}
}
// 运行结果
6
进程已结束,退出代码 0
getAndIncrement
value.getAndIncrement() 等同于 value++
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger(5);
System.out.println(value.getAndIncrement());
System.out.println(value.get());
}
}
// 运行结果
5
6
进程已结束,退出代码 0
decrementAndGet
value.decrementAndGet() 等同于 --value
getAndDecrement
value.getAndDecrement() 等同于 value--
addAndGet
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger(5);
System.out.println(value.addAndGet(-4)); // 1 <- 5+(-4)=1
}
}
// 运行结果
1
进程已结束,退出代码 0
【6.1.2 解决思路 - 无锁】优化
package com.rui.six;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Test1 {
public static void main(String[] args) {
Account.demo(new AccountCas(10000));
}
}
class AccountCas implements Account {
private AtomicInteger balance;
public AccountCas(Integer balance) {
this.balance = new AtomicInteger(balance);
}
@Override
public Integer getBalance() {
return balance.get();
}
@Override
public void withdraw(Integer amount) {
balance.addAndGet(-amount);
}
}
interface Account {
// 获取余额
Integer getBalance();
// 取款
void withdraw(Integer amount);
/**
* 方法内会启动 1000 个线程,每个线程做取款 10 元的操作
* 如果初始余额为 10000 元,那么终止余额应当是 0 元
*/
static void demo(Account account) {
List<Thread> ts = new ArrayList<>();
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(10);
}));
}
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println("终止余额 = " + account.getBalance()
+ ";cost " + (end - start) / 1000_000 + " ms");
}
}
// 运行结果
终止余额 = 0;cost 96 ms
进程已结束,退出代码 0
getAndAdd
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger(5);
System.out.println(value.getAndAdd(5));
System.out.println(value.get());
}
}
// 运行结果
5
10
进程已结束,退出代码 0
updateAndGet
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger(5);
System.out.println(value.updateAndGet(x -> x * -1)); // -5 <- 5*(-1)=-5
}
}
// 运行结果
-5
进程已结束,退出代码 0
getAndUpdate
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
public class Test2 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger(5);
System.out.println(value.getAndUpdate(x -> x + 2));
System.out.println(value.get());
}
}
// 运行结果
5
7
进程已结束,退出代码 0
package com.rui.six;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;
public class Test3 {
public static void main(String[] args) {
AtomicInteger value = new AtomicInteger(5);
System.out.println(getAndUpdate(value, x -> x * 2));
System.out.println(value.get());
}
public static int getAndUpdate(AtomicInteger value, IntUnaryOperator operator) {
while (true) {
int prev = value.get();
int next = operator.applyAsInt(prev);
if (value.compareAndSet(prev, next)) {
return prev;
}
}
}
}
// 运行结果
5
10
进程已结束,退出代码 0
6.4 原子引用
AtomicReference、AtomicMarkableReference、AtomicStampedReference
AtomicReference
package com.rui.six;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public class Test4 {
public static void main(String[] args) {
Account4.demo(new AccountCas4(new BigDecimal("10000")));
}
}
class AccountCas4 implements Account4 {
private AtomicReference<BigDecimal> balance;
public AccountCas4(BigDecimal balance) {
this.balance = new AtomicReference<>(balance);
}
@Override
public BigDecimal getBalance() {
return balance.get();
}
@Override
public void withdraw(BigDecimal amount) {
while (true) {
BigDecimal prev = balance.get();
BigDecimal next = prev.subtract(amount);
if (balance.compareAndSet(prev, next)) {
break;
}
}
}
}
interface Account4 {
// 获取余额
BigDecimal getBalance();
// 取款
void withdraw(BigDecimal amount);
/**
* 方法内会启动 1000 个线程,每个线程做取款 10 元的操作
* 如果初始余额为 10000 元,那么终止余额应当是 0 元
*/
static void demo(Account4 account) {
List<Thread> ts = new ArrayList<>();
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(BigDecimal.TEN);
}));
}
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println("终止余额 = " + account.getBalance()
+ ";cost " + (end - start) / 1000_000 + " ms");
}
}
// 运行结果
终止余额 = 0;cost 116 ms
进程已结束,退出代码 0
package com.rui.six;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j(topic = "c.Test5")
public class Test5 {
static AtomicReference<String> value = new AtomicReference<>("A");
public static void main(String[] args) throws InterruptedException {
log.debug("main begin...");
other();
Thread.sleep(1000);
log.debug("A -> C : {}", value.compareAndSet(value.get(), "C"));
}
public static void other() throws InterruptedException {
new Thread(() -> {
log.debug("t1 begin...");
log.debug("A -> B : {}", value.compareAndSet(value.get(), "B"));
}, "t1").start();
Thread.sleep(500);
new Thread(() -> {
log.debug("t2 begin...");
log.debug("B -> A : {}", value.compareAndSet(value.get(), "A"));
}, "t2").start();
}
}
// 运行结果
11:46:15 [main] c.Test5 - main begin...
11:46:15 [t1] c.Test5 - t1 begin...
11:46:15 [t1] c.Test5 - A -> B : true
11:46:16 [t2] c.Test5 - t2 begin...
11:46:16 [t2] c.Test5 - B -> A : true
11:46:17 [main] c.Test5 - A -> C : true
进程已结束,退出代码 0
上述代码中,对 value 的修改(ABA 问题)未能被检测到,那么如何检测到这种修改呢?
AtomicStampedReference
AtomicStampedReference
package com.rui.six;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicStampedReference;
@Slf4j(topic = "c.Test5")
public class Test6 {
static AtomicStampedReference<String> value = new AtomicStampedReference<>("A", 0);
public static void main(String[] args) throws InterruptedException {
log.debug("main begin...");
int stamp = value.getStamp();
other();
Thread.sleep(1000);
log.debug("stamp = {}", stamp);
log.debug("A -> C : {}", value.compareAndSet(value.getReference(), "C", stamp, stamp + 1));
}
public static void other() throws InterruptedException {
new Thread(() -> {
log.debug("t1 begin...");
log.debug("A -> B : {}", value.compareAndSet(value.getReference(), "B", value.getStamp(), value.getStamp() + 1));
}, "t1").start();
Thread.sleep(500);
new Thread(() -> {
log.debug("t2 begin...");
log.debug("B -> A : {}", value.compareAndSet(value.getReference(), "A", value.getStamp(), value.getStamp() + 1));
}, "t2").start();
}
}
// 运行结果
11:55:23 [main] c.Test5 - main begin...
11:55:23 [t1] c.Test5 - t1 begin...
11:55:23 [t1] c.Test5 - A -> B : true
11:55:23 [t2] c.Test5 - t2 begin...
11:55:23 [t2] c.Test5 - B -> A : true
11:55:24 [main] c.Test5 - stamp = 0
11:55:24 [main] c.Test5 - A -> C : false
进程已结束,退出代码 0
AtomicMarkableReference
package com.rui.six;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j(topic = "c.Test7")
public class Test7 {
static AtomicMarkableReference<String> amr = new AtomicMarkableReference<>("垃圾溢出", true);
public static void main(String[] args) throws InterruptedException {
other();
Thread.sleep(1000);
log.debug("T/F?{}",amr.compareAndSet(amr.getReference(),"清理垃圾",true,false));
}
public static void other() {
new Thread(() -> {
log.debug("T/F?{}",amr.compareAndSet(amr.getReference(),"清理垃圾",true,false));
log.debug("{}",amr.getReference());
},"保洁阿姨 1 号").start();
new Thread(() -> {
log.debug("T/F?{}",amr.compareAndSet(amr.getReference(),"垃圾溢出",true,false));
log.debug("{}",amr.getReference());
},"保洁阿姨 2 号").start();
}
}
class GarbageBag {
private String garbageBag;
public GarbageBag(String garbageBag) {
this.garbageBag = garbageBag;
}
public String getGarbageBag() {
return garbageBag;
}
@Override
public String toString() {
return "GarbageBag{" +
"garbageBag='" + garbageBag + '\'' +
'}';
}
}
// 运行结果
12:06:27 [保洁阿姨 1 号] c.Test7 - T/F?true
12:06:27 [保洁阿姨 2 号] c.Test7 - T/F?false
12:06:27 [保洁阿姨 2 号] c.Test7 - 清理垃圾
12:06:27 [保洁阿姨 1 号] c.Test7 - 清理垃圾
12:06:28 [main] c.Test7 - T/F?false
进程已结束,退出代码 0
6.5 原子数组
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
非线程安全
package com.rui.six;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.*;
public class Test8 {
public static void main(String[] args) {
demo(
() -> new int[10],
(array) -> array.length,
(array, index) -> array[index]++,
(array) -> System.out.println(Arrays.toString(array))
);
}
/**
* 参数1,提供数组、可以是线程不安全数组或线程安全数组
* 参数2,获取数组长度的方法
* 参数3,自增方法,回传 array、index
* 参数4,打印数组的方法
*/
// Supplier 提供者 无中生有 () -> 结果
// Function 函数 一个参数一个结果 (参数) -> 结果,BiFunction (参数1,参数2) -> 结果
// Consumer 消费者 一个参数没结果 (参数) -> void,BiConsumer (参数1,参数2) -> void
private static <T> void demo(
Supplier<T> arraySupplier,
Function<T, Integer> lengthFun,
BiConsumer<T, Integer> putConsumer,
Consumer<T> printConsumer) {
List<Thread> list = new ArrayList<>();
T array = arraySupplier.get();
int length = lengthFun.apply(array);
for (int i = 0; i < length; i++) {
// 每个线程对数组做 10000 次操作
list.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
putConsumer.accept(array, j % length);
}
}));
}
list.forEach(t -> t.start()); // 启动所有线程
list.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}); // 等所有线程结束
printConsumer.accept(array);
}
}
// 某次运行结果
[9496, 9462, 9530, 9506, 9486, 9509, 9522, 9494, 9447, 9483]
进程已结束,退出代码 0
线程安全 - 以 AtomicIntegerArray 为例
package com.rui.six;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.*;
public class Test8 {
public static void main(String[] args) {
demo(
() -> new int[10],
(array) -> array.length,
(array, index) -> array[index]++,
(array) -> System.out.println(Arrays.toString(array))
);
demo(
() -> new AtomicIntegerArray(10),
(array) -> array.length(),
(array, index) -> array.getAndIncrement(index),
(array) -> System.out.println(array)
);
}
/**
* 参数1,提供数组、可以是线程不安全数组或线程安全数组
* 参数2,获取数组长度的方法
* 参数3,自增方法,回传 array、index
* 参数4,打印数组的方法
*/
// Supplier 提供者 无中生有 () -> 结果
// Function 函数 一个参数一个结果 (参数) -> 结果,BiFunction (参数1,参数2) -> 结果
// Consumer 消费者 一个参数没结果 (参数) -> void,BiConsumer (参数1,参数2) -> void
private static <T> void demo(
Supplier<T> arraySupplier,
Function<T, Integer> lengthFun,
BiConsumer<T, Integer> putConsumer,
Consumer<T> printConsumer) {
List<Thread> list = new ArrayList<>();
T array = arraySupplier.get();
int length = lengthFun.apply(array);
for (int i = 0; i < length; i++) {
// 每个线程对数组做 10000 次操作
list.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
putConsumer.accept(array, j % length);
}
}));
}
list.forEach(t -> t.start()); // 启动所有线程
list.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}); // 等所有线程结束
printConsumer.accept(array);
}
}
// 某次运行结果
[9076, 9056, 9086, 9076, 9036, 9048, 9810, 9772, 9794, 9806]
[10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000]
进程已结束,退出代码 0
6.7 字段更新器
AtomicReferenceFieldUpdater
修改成员变量
package com.rui.six;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class Test9 {
public static void main(String[] args) {
Student stu = new Student();
AtomicReferenceFieldUpdater arfu = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
System.out.println(arfu.compareAndSet(stu, null, "张三"));
System.out.println(stu);
}
}
class Student {
public volatile String name;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
// 运行结果
true
Student{name='张三'}
进程已结束,退出代码 0
6.8 原子累加器
package com.rui.six;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class Test10 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
demo(
() -> new AtomicLong(),
(adder) -> adder.incrementAndGet()
);
}
System.out.println("================== 一条华丽的分割线 ==================");
for (int i = 0; i < 5; i++) {
demo(
() -> new LongAdder(),
(adder) -> adder.increment()
);
}
}
private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {
T adder = adderSupplier.get();
long start = System.nanoTime();
List<Thread> list = new ArrayList<>();
// 40 个线程,每个线程累加 50 W
for (int i = 0; i < 40; i++) {
list.add(new Thread(() -> {
for (int j = 0; j < 500000; j++) {
action.accept(adder);
}
}));
}
list.forEach(t -> t.start());
list.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println(adder + ",cost:" + (end - start) / 1000_000);
}
}
// 某次运行结果
20000000,cost:180
20000000,cost:165
20000000,cost:163
20000000,cost:161
20000000,cost:161
================== 一条华丽的分割线 ==================
20000000,cost:18
20000000,cost:9
20000000,cost:9
20000000,cost:25
20000000,cost:18
进程已结束,退出代码 0
说些废话
本篇文章为博主日常学习记录,故而会概率性地存在各种错误,若您在浏览过程中发现一些,请在评论区指正,望我们共同进步,谢谢!