文章目录
前言
本文总结了 在多线程场景下递增数字num,每次调用一次递增一次 不同的实现方案。
使用悲观锁和乐观锁两种方案,代码使用Java实现。
记录以用于线程和多并发知识的学习。
一、悲观锁?
方法一:使用synchronized关键字
方法二:使用 Lock 接口,如 ReentrantLock实现类
ReentrantLock:一种可重入互斥 Lock ,其基本行为和语义与使用方法和语句 synchronized 的隐式监视器锁相同,但具有扩展功能。
public class IncrementExample {
private int num;
public int getNum() {
return num;
}
/**
* 方法一:使用synchronized关键字
*/
public synchronized void incrementNum() {
num++; // 递增数字
}
/**
* 方法二:使用‘Lock’接口,如 'ReentrantLock'
*/
private Lock lock = new ReentrantLock();
public void incrementByLock() {
lock.lock();
try {
num++;
} finally {
lock.unlock();
}
}
}
测试代码
public class IncrementTest {
public static void main(String[] args) throws InterruptedException {
final int numThread = 10;
final int numIncrementPerThread = 1000;
// 悲观锁
IncrementExample example = new IncrementExample();
// 乐观锁
// IncrementExamplePositive example = new IncrementExamplePositive();
// Atomic类
IncrementExampleAtomic example = new IncrementExampleAtomic();
Thread[] threads = new Thread[numThread];
for (int i=0; i < numThread; i++) {
threads[i] = new Thread(() -> {
for (int j=0; j<numIncrementPerThread; j++) {
example.incrementByLock();
// System.out.println(Thread.currentThread().getName() +"---"+ example.getNum());
}
});
threads[i].start();
}
for (Thread thread : threads) {
thread.join();
}
// 输出最终结果
System.out.println("Final value of num: " + example.getNum());
}
二、乐观锁
方法三:使用CAS(Compare-And-Swap)
代码如下(示例):
public class IncrementExamplePositive {
private volatile int num;
// private int num;
public void incrementNum() {
int current;
int next;
do {
current = num; // 获取当前值
next = current + 1; // 计算递增后的值
} while (!compareAndSet(current, next)); // 比较并设置,知道成功递增
}
private boolean compareAndSet(int expect, int update) {
if (num == expect) {
num = update; // 设置新值
return true;
}
return false;
}
public int getNum() {
return num;
}
}
在上述示例中,使用volatile关键字修饰num变量,以确保在多线程环境下的可见性。在**incrementNum()方法中,我们使用compareAndSet()**方法进行比较和设置操作,直到成功递增。
compareAndSet()方法使用synchronized关键字进行同步,确保只有一个线程可以进入方法内部进行操作。在方法内部,我们首先检查num的当前值是否与期望值相等,如果相等,则将num更新为新值。
通过结合volatile关键字和CAS操作,我们可以实现乐观锁的效果,在多线程环境下安全地递增数字。需要注意的是,**这种方式可能存在竞争和自旋的情况,因此在高并发场景下,性能可能较差。**在这种情况下,使用AtomicInteger等原子类会更为高效。
即,下面的方法四。
方法四:使用AtomicInteger类
public class IncrementExampleAtomic {
private AtomicInteger num = new AtomicInteger();
public void incrementNum() {
num.incrementAndGet(); // 递增数字
}
public int getNum() {
return num.get();
}
}
请注意,使用乐观锁的方式在高并发场景下可能存在竞争和自旋的情况,性能可能较差。在这种情况下,使用AtomicInteger等原子类可能更加高效。
乐观锁测试代码同上。
总结
实际测试,三种方法性能都差不多。就是三种不同的写法。
CAS思想,在Java并发思想中很重要。