详解Java中的悲观锁与乐观锁
什么是锁?
在多线程编程中,为了保证线程安全和数据一致性,我们常常采用锁机制。锁顾名思义就是在一段代码区域加上一个锁,使得同一时刻只有一个线程可以访问该代码区域。Java中的锁机制主要有两种:悲观锁和乐观锁。
悲观锁
悲观锁的思想就是认为并发情况下不同线程之间会发生冲突,因此在整个处理过程中,都加上了同步锁,让线程独占资源,其他线程等待。
Java中常见的悲观锁实现方式是synchronized关键字,例如:
public synchronized void method(){
// 该方法的代码块
}
synchronized关键字会自动加锁,当线程执行synchronized代码块时,其他线程无法访问该代码块,只能等待。
乐观锁
乐观锁的思想则是相反的,它认为并发情况下不同线程之间不会发生冲突,因此不需要加锁。在更新数据时,判断该数据有没有被其他线程修改,若该数据被修改,则暂停该次操作,重新读取数据并重试。
Java中的乐观锁实现方式一般是通过CAS(Compare And Swap)来实现,例如:
public class Counter {
private AtomicInteger num = new AtomicInteger(0);
public void increment() {
int newVal;
do {
newVal = num.get() + 1;
} while(!num.compareAndSet(num.get(), newVal));
}
}
CAS先获取内存中的值,然后基于获取的值来计算新值,最后通过CAS操作来尝试更新内存中的值。如果这个值不是期望的,那么修改失败,就需要重试。
悲观锁与乐观锁的选择
在选择使用悲观锁还是乐观锁时,需要根据实际情况来进行考虑。
悲观锁适合于写操作比较多的情况,因为写操作需要独占资源。悲观锁在实现简单的同时也存在着性能问题,因为当访问量大时,悲观锁会降低系统的吞吐量。
乐观锁适合于读操作比较多的情况,因为读操作不需要独占资源,多个线程可以共同读取一个数据。并且乐观锁的实现方式可以避免了加锁引起的性能问题。
示例
下面两个示例演示了悲观锁和乐观锁的实现方式。其中线程安全的计数器Counter类用于演示两种锁的实现方式。
悲观锁示例
public class PessimisticLockExample {
private static int value = 0;
public static synchronized void increment() {
value++;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("PessimisticLockExample value: " + value);
}
}
乐观锁示例
public class OptimisticLockExample {
private static AtomicInteger value = new AtomicInteger(0);
public static void increment() {
int oldValue;
int newValue;
do {
oldValue = value.get();
newValue = oldValue + 1;
} while(!value.compareAndSet(oldValue, newValue));
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
increment();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("OptimisticLockExample value: " + value);
}
}
总结
悲观锁的实现方式简单直接,但存在性能问题,适合于写操作比较多的情况。乐观锁的实现方式可以避免加锁带来的性能问题,适合于读操作比较多的情况。在实际情况中,需要根据具体情况选择合适的锁实现方式来保证线程安全和数据一致性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Java中的悲观锁与乐观锁 - Python技术站