这里记录一下线程安全以及几种锁的示例
1.线程不安全的示例(线程不安全):
private Integer store = 10000;
/**
* @Description: 线程不安全
* @Param: []
* @Author: tom
* @Date: 2023/12/22
* @Return int
*/
public int notSafe() {
store--;
return store;
}
@Test
public void notSafeTest() throws InterruptedException {
for (int i = 1; i <= 10000; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"_线程不安全扣减后:"+notSafe());
}).start();
}
Thread.sleep(5000);
System.out.println("最终:"+store);
}
库存1W然后,启用1W个线程进行扣减,结果居然有剩余,每次执行结果不一样,反正就是不为0,这时候老板要扣你工资了!!!
2.解决的方法当然有
a.使用CAS锁;
private AtomicInteger store1 = new AtomicInteger(10000);
/**
* @Description: 线程安全
* @Param: []
* @Author: tom
* @Date: 2023/12/22
* @Return int
*/
public int casLock() {
int oldValue = store1.get();
int newValue = oldValue - 1;
while (!store1.compareAndSet(oldValue, newValue)) {
// 如果CAS失败,则重新读取当前值并尝试再次CAS
oldValue = store1.get();
newValue = oldValue - 1;
}
return newValue;
}
@Test
public void casLockTeset() throws InterruptedException {
for (int i = 1; i <= 10000; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"_cas扣减后:"+casLock());
}).start();
}
Thread.sleep(5000);
System.out.println("最终:"+store1);
}
执行结果:
最终结果为0了,你又可以继续摸鱼了。
b.使用同步锁synchronized:
private Integer store2 = 10000;
/**
* @Description: synchronizedLock锁
* @Param: []
* @Author: tom
* @Date: 2023/12/22
* @Return int
*/
public synchronized int synchronizedLock() {
store2--;
return store2;
}
@Test
public void synchronizedLockTest() throws InterruptedException {
for (int i = 1; i <= 10000; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"_synchronized扣减后:"+synchronizedLock());
}).start();
}
Thread.sleep(5000);
System.out.println("最终:"+store2);
}
执行结果:
结果为0,你又可以快乐地刷抖音了。
c.使用重入锁ReentrantLock
private final ReentrantLock lock = new ReentrantLock();
private int count = 10000;
public int reentrantLockCount() {
lock.lock(); // 获取锁
try {
count--;
} finally {
lock.unlock(); // 释放锁
}
return count;
}
@Test
public void reentrantLockTest() throws InterruptedException {
for (int i = 1; i <= 10000; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"_count:"+reentrantLockCount());
}).start();
}
Thread.sleep(5000);
System.out.println("最终:"+count);
}
运行结果:
最终结果为0了,你又可以继续摸鱼了。
4.特别注意:
以上三种方法只是适用于单节点服务,如果系统是多节点服务,以上做法就不能保证线程安全了!
需要引入分布式事务(如Alibaba 的 Seata)和分布式锁(如Redisson)。
分布式事务和分布式锁示例:后续补充......