线程安全的分辨
如果一段代码是线程安全的,那么它不存在竞态条件。只有当多个线程更新共享资源时,才会发生竞态条件。
原子性问题的演示
// 两个线程,对 i 变量进行递增操作
public class LockDemo {
//volatile只能保证线程的可见性,不能保证线程的原子性.
volatile int i = 0;
public void add() {
i++;// 三个步骤
}
public static void main(String[] args) throws InterruptedException {
LockDemo ld = new LockDemo();
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 10000; j++) {
ld.add();
}
}).start();
}
Thread.sleep(2000L);
System.out.println(ld.i);
}
}
CAS方式去解决
public class LockDemo1 {
volatile int value = 0;
static Unsafe unsafe; // 直接操作内存,修改对象,数组内存....强大的API
private static long valueOffset;
static {
try {
// 反射技术获取unsafe值
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
// 获取到 value 属性偏移量(用于定于value属性在内存中的具体地址)
valueOffset = unsafe.objectFieldOffset(LockDemo1.class
.getDeclaredField("value"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void add() {
// TODO xx00
// i++;// JAVA 层面三个步骤
// CAS + 循环 重试
int current;
do {
// 操作耗时的话, 那么 线程就会占用大量的CPU执行时间
current = unsafe.getIntVolatile(this, valueOffset);
} while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));
// 可能会失败
}
public static void main(String[] args) throws InterruptedException {
LockDemo1 ld = new LockDemo1();
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 10000; j++) {
ld.add();
}
}).start();
}
Thread.sleep(2000L);
System.out.println(ld.value);
}
}
使用atomic工具类
// 两个线程,对 i 变量进行递增操作
public class LockDemo {
// volatile int i = 0;
AtomicInteger i = new AtomicInteger(0);
public void add() {
// TODO xx00
// i++;// 三个步骤
i.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
LockDemo ld = new LockDemo();
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 10000; j++) {
ld.add();
}
}).start();
}
Thread.sleep(2000L);
System.out.println(ld.i);
}
}
Jdk新的计数器
// 测试用例: 同时运行2秒,检查谁的次数最多
public class LongAdderDemo {
private long count = 0;
// 同步代码块的方式
public void testSync() throws InterruptedException {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
long starttime = System.currentTimeMillis();
while (System.currentTimeMillis() - starttime < 2000) { // 运行两秒
synchronized (this) {
++count;
}
}
long endtime = System.currentTimeMillis();
System.out.println("SyncThread spend:" + (endtime - starttime) + "ms" + " v" + count);
}).start();
}
}
// Atomic方式
private AtomicLong acount = new AtomicLong(0L);
public void testAtomic() throws InterruptedException {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
long starttime = System.currentTimeMillis();
while (System.currentTimeMillis() - starttime < 2000) { // 运行两秒
acount.incrementAndGet(); // acount++;
}
long endtime = System.currentTimeMillis();
System.out.println("AtomicThread spend:" + (endtime - starttime) + "ms" + " v-" + acount.incrementAndGet());
}).start();
}
}
// LongAdder 方式
private LongAdder lacount = new LongAdder();
public void testLongAdder() throws InterruptedException {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
long starttime = System.currentTimeMillis();
while (System.currentTimeMillis() - starttime < 2000) { // 运行两秒
lacount.increment();
}
long endtime = System.currentTimeMillis();
System.out.println("LongAdderThread spend:" + (endtime - starttime) + "ms" + " v-" + lacount.sum());
}).start();
}
}
public static void main(String[] args) throws InterruptedException {
LongAdderDemo demo = new LongAdderDemo();
demo.testSync();
demo.testAtomic();
demo.testLongAdder();
}
}
- 执行结果
SyncThread spend:2000ms v21103457
SyncThread spend:2000ms v21103457
SyncThread spend:2000ms v21103457
AtomicThread spend:2000ms v-87109275
LongAdderThread spend:2000ms v-194372483
AtomicThread spend:2000ms v-87919296
LongAdderThread spend:2000ms v-194854387
AtomicThread spend:2000ms v-88120307
LongAdderThread spend:2000ms v-197359712
LongAdder的增强版
// LongAdder增强版,处理累加之外,可以自行定义其他计算
public class LongAccumulatorDemo {
public static void main(String[] args) throws InterruptedException {
LongAccumulator accumulator = new LongAccumulator(new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
// 返回最大值,这就是自定义的计算
return left < right ? left : right;
}
}, 0);
// 1000个线程
for (int i = 0; i < 1000; i++) {
int finalI = i;
new Thread(() -> {
accumulator.accumulate(finalI); // 此处实际就是执行上面定义的操作
}).start();
}
Thread.sleep(2000L);
System.out.println(accumulator.longValue()); // 打印出结果
}
}
ABA问题的案例分析与解决方法
- ABA问题
// aba 问题
// 重复操作 / 过时操作。
public class AbaDemo {
// 模拟充值
// 有3个线程在给用户充值,当用户余额少于20时,就给用户充值20元。
// 有100个线程在消费,每次消费10元。用户初始有9元
static AtomicInteger money = new AtomicInteger(19);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
int current;
do {
current = money.get();// 获取当前内存中的值
} while (!money.compareAndSet(current, current + 20)); // CAS
}).start();
}
new Thread(() -> {
for (int i = 0; i < 100; i++) {
while (true) {
Integer m = money.get();
if (m > 10) {
if (money.compareAndSet(m, m - 10)) {
System.out.println("消费10元,余额:" + money.get());
break;
}
} else {
break;
}
}
try {
Thread.sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
}
}).start();
}
}
- 展示结果
消费10元,余额:69
消费10元,余额:59
消费10元,余额:49
消费10元,余额:39
消费10元,余额:29
消费10元,余额:19
消费10元,余额:9
- ABA问题的解决
public class AbaDemo1 {
// 模拟充值
// 有3个线程在给用户充值,当用户余额少于20时,就给用户充值20元。
// 有100个线程在消费,每次消费10元。用户初始有9元
static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 0);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
final int timestamp = money.getStamp();
new Thread(() -> {
while (true) {
while (true) {
Integer m = money.getReference();
if (m < 20) {
if (money.compareAndSet(m, m + 20, timestamp,
timestamp + 1)) {
System.out.println("充值成功,余额:"
+ money.getReference());
break;
}
} else {
break;
}
}
}
}).start();
}
new Thread(() -> {
for (int i = 0; i < 100; i++) {
while (true) {
int timestamp = money.getStamp();
Integer m = money.getReference();
if (m > 10) {
if (money.compareAndSet(m, m - 10, timestamp,
timestamp + 1)) {
System.out.println("消费10元,余额:"
+ money.getReference());
break;
}
} else {
break;
}
}
try {
Thread.sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
}
}).start();
}
}
- 案例结果
充值成功,余额:39
消费10元,余额:29
消费10元,余额:19
消费10元,余额:9