java中乐观锁,悲观锁,公平锁,非公平锁,死锁,可重入锁,同步锁,读写锁,表锁,行锁,间隙锁,排它锁,自旋锁 - 面试宝典
Java中的锁机制包括乐观锁、悲观锁、公平锁、非公平锁、死锁、可重入锁、同步锁、读写锁、表锁、行锁、间隙锁、排它锁和自旋锁。下面逐一介绍:
- 乐观锁:假设多个线程之间不会发生冲突,每个线程在进行操作时都不会加锁,只有在提交操作时才会对数据进行冲突检测。
- 悲观锁:假设多个线程之间会发生冲突,每个线程在进行操作时先加锁,确保同一时间只有一个线程能访问共享资源。
- 公平锁:按照线程请求锁的顺序来分配锁,保证所有线程公平竞争锁的机会。
- 非公平锁:不按照线程请求锁的顺序来分配锁,允许线程插队抢占锁。
- 死锁:两个或多个线程互相持有对方所需的资源,导致所有线程都无法继续执行的情况。
- 可重入锁:也称为递归锁,同一个线程可以多次获得同一个锁,避免死锁。
- 同步锁:通过synchronized关键字实现的锁,保证同一时间只有一个线程能访问共享资源。
- 读写锁:也称为共享锁和排他锁,允许多个线程同时读取共享资源,但只有一个线程能写入共享资源。
- 表锁:对整个表进行锁定,防止其他事务对表进行修改操作。
- 行锁:对表中的行进行锁定,只锁定需要修改的行,提高并发性能。
- 间隙锁:锁定索引范围而不是实际的数据行,用于防止其他事务插入索引范围内的数据。
- 排它锁:也称为独占锁,只允许一个线程独占资源,其他线程无法访问。
- 自旋锁:在获取锁时,线程不会立即阻塞,而是循环检查锁的状态,直到获取到锁为止。 同步锁和异步锁是指在多线程环境下对共享资源进行操作的方式。同步锁是指在一个线程执行共享资源的操作时,其他线程需要等待,直到该线程释放锁;异步锁是指在一个线程执行共享资源的操作时,其他线程不需要等待,可以继续执行自己的操作。 排它锁是一种只允许一个线程独占资源的锁,其他线程无法访问。自旋锁是一种在获取锁时循环检查锁的状态的锁,避免线程阻塞等待锁的释放。
下面是Java中几种锁的代码示例:
- 同步锁(使用synchronized关键字):
javaCopy codepublic class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
}
- 可重入锁(使用ReentrantLock类):
javaCopy codeimport java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
- 读写锁(使用ReentrantReadWriteLock类):
javaCopy codeimport java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private int count = 0;
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
lock.readLock().lock();
try {
// 读取共享资源
} finally {
lock.readLock().unlock();
}
}
public void write() {
lock.writeLock().lock();
try {
// 写入共享资源
} finally {
lock.writeLock().unlock();
}
}
}
- 自旋锁(使用AtomicInteger类的compareAndSet方法):
javaCopy codeimport java.util.concurrent.atomic.AtomicInteger;
public class SpinLockExample {
private AtomicInteger lock = new AtomicInteger(0);
public void lock() {
while (!lock.compareAndSet(0, 1)) {
// 自旋等待锁的释放
}
}
public void unlock() {
lock.set(0);
}
}
以上是一些锁的基本代码示例,具体使用时需要根据实际场景进行适当的调整和扩展。
- 公平锁和非公平锁(使用ReentrantLock类):
javaCopy codeimport java.util.concurrent.locks.ReentrantLock;
public class FairNonFairLockExample {
private int count = 0;
// 公平锁
private ReentrantLock fairLock = new ReentrantLock(true);
// 非公平锁
private ReentrantLock nonFairLock = new ReentrantLock(false);
public void incrementWithFairLock() {
fairLock.lock();
try {
count++;
} finally {
fairLock.unlock();
}
}
public void incrementWithNonFairLock() {
nonFairLock.lock();
try {
count++;
} finally {
nonFairLock.unlock();
}
}
}
- 死锁(简化示例):
javaCopy codepublic class DeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
synchronized (lock2) {
// 执行操作
}
}
}
public void method2() {
synchronized (lock2) {
synchronized (lock1) {
// 执行操作
}
}
}
}
请注意,死锁是一种严重的问题,上述示例只是简化了死锁的情况,请在实际开发中避免出现死锁的情况。 这些代码示例仅作为理解锁概念和使用的参考,具体使用时需要根据实际情况进行适当的调整和扩展。
- 乐观锁和悲观锁(使用版本号或时间戳实现):
javaCopy codeimport java.util.concurrent.atomic.AtomicStampedReference;
public class OptimisticPessimisticLockExample {
private AtomicStampedReference<Integer> value = new AtomicStampedReference<>(0, 0);
// 乐观锁
public void optimisticUpdate() {
int currentStamp = value.getStamp();
int currentValue = value.getReference();
// 在更新前检查版本号
if (value.compareAndSet(currentValue, currentValue + 1, currentStamp, currentStamp + 1)) {
// 更新成功
} else {
// 更新失败,进行相应处理
}
}
// 悲观锁
public void pessimisticUpdate() {
synchronized (this) {
int currentValue = value.getReference();
// 进行更新操作
value.set(currentValue + 1, value.getStamp() + 1);
}
}
}
- 分段锁(使用ConcurrentHashMap类):
javaCopy codeimport java.util.concurrent.ConcurrentHashMap;
public class StripedLockExample {
private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
private int segments = 16;
private Object[] locks = new Object[segments];
public StripedLockExample() {
for (int i = 0; i < segments; i++) {
locks[i] = new Object();
}
}
public void update(String key) {
int segment = Math.abs(key.hashCode() % segments);
synchronized (locks[segment]) {
// 进行更新操作
map.put(key, map.getOrDefault(key, 0) + 1);
}
}
}
乐观锁和悲观锁是在并发编程中常用的两种思路,分段锁是一种在高并发场景下提高性能的技术。这些代码示例仅作为理解锁概念和使用的参考,具体使用时需要根据实际情况进行适当的调整和扩展。
- 自旋锁的优化(使用自适应自旋和锁消除):
javaCopy codeimport java.util.concurrent.atomic.AtomicInteger;
public class SpinLockOptimizationExample {
private AtomicInteger lock = new AtomicInteger(0);
public void lock() {
int spinCount = 0;
while (true) {
if (lock.compareAndSet(0, 1)) {
return;
} else {
if (spinCount < 10) {
spinCount++;
} else {
Thread.yield(); // 自适应自旋
}
}
}
}
public void unlock() {
lock.set(0);
}
}
- 读写锁的升降级(使用StampedLock类):
javaCopy codeimport java.util.concurrent.locks.StampedLock;
public class ReadWriteLockUpgradeExample {
private int value = 0;
private StampedLock lock = new StampedLock();
public void read() {
long stamp = lock.tryOptimisticRead();
int currentValue = value;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
currentValue = value;
} finally {
lock.unlockRead(stamp);
}
}
// 读取共享资源
}
public void write() {
long stamp = lock.writeLock();
try {
// 写入共享资源
} finally {
lock.unlockWrite(stamp);
}
}
}
以上是一些锁的优化和高级用法的代码示例,具体使用时需要根据实际场景进行适当的调整和扩展。
- 条件锁(使用Condition类):
javaCopy codeimport java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionLockExample {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean flag = false;
public void waitMethod() throws InterruptedException {
lock.lock();
try {
while (!flag) {
condition.await();
}
// 执行操作
} finally {
lock.unlock();
}
}
public void signalMethod() {
lock.lock();
try {
flag = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
}
- 闭锁(使用CountDownLatch类):
javaCopy codeimport java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
private static final int THREAD_COUNT = 3;
private CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
public void doWork() throws InterruptedException {
// 创建并启动工作线程
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread(() -> {
// 执行工作任务
latch.countDown();
});
thread.start();
}
// 等待所有工作线程完成
latch.await();
// 所有工作线程完成后继续执行其他操作
}
}
以上是一些常见的锁的使用场景,具体使用时需要根据实际需求进行适当调整和扩展。同时也要注意锁的使用要避免死锁和竞态条件等并发问题。
- 读写锁的降级(使用ReentrantReadWriteLock类):
javaCopy codeimport java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDowngradeExample {
private int value = 0;
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void readAndWrite() {
lock.writeLock().lock();
try {
// 写入共享资源
value++;
// 降级为读锁
lock.readLock().lock();
} finally {
lock.writeLock().unlock();
}
try {
// 读取共享资源
System.out.println(value);
} finally {
lock.readLock().unlock();
}
}
}
- 信号量(使用Semaphore类):
javaCopy codeimport java.util.concurrent.Semaphore;
public class SemaphoreExample {
private Semaphore semaphore = new Semaphore(3);
public void doWork() throws InterruptedException {
// 获取信号量许可
semaphore.acquire();
try {
// 执行任务
} finally {
// 释放信号量许可
semaphore.release();
}
}
}
以上是一些进阶的锁及并发工具的使用示例,具体使用时需要根据实际情况进行适当的调整和扩展。同时,要注意合理使用锁和并发工具,避免产生死锁、竞态条件等问题。
- 阻塞队列(使用BlockingQueue接口):
javaCopy codeimport java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class BlockingQueueExample {
private BlockingQueue<String> queue = new LinkedBlockingQueue<>();
public void produce(String item) throws InterruptedException {
queue.put(item);
}
public String consume() throws InterruptedException {
return queue.take();
}
}
- 倒计时门闩(使用CountDownLatch类):
javaCopy codeimport java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
private CountDownLatch latch = new CountDownLatch(3);
public void doWork() throws InterruptedException {
// 创建并启动工作线程
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(() -> {
// 执行工作任务
latch.countDown();
});
thread.start();
}
// 等待所有工作线程完成
latch.await();
// 所有工作线程完成后继续执行其他操作
}
}
以上是一些常见的并发工具的使用示例,具体使用时需要根据实际需求进行适当调整和扩展。同时也要注意并发工具的正确使用,避免出现线程安全问题。
- 循环屏障(使用CyclicBarrier类):
javaCopy codeimport java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
private static final int THREAD_COUNT = 3;
private CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT, () -> {
// 所有线程都到达屏障后执行的操作
System.out.println("All threads have reached the barrier");
});
public void doWork() {
// 创建并启动工作线程
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread(() -> {
try {
// 执行工作任务
System.out.println("Thread has reached the barrier");
barrier.await();
// 所有线程都到达屏障后继续执行其他任务
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
thread.start();
}
}
}
- 定时任务(使用ScheduledExecutorService接口):
javaCopy codeimport java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledTaskExample {
private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
public void scheduleTask() {
executor.schedule(() -> {
// 执行定时任务
}, 1, TimeUnit.SECONDS);
}
}
以上是一些高级的并发工具的使用示例,具体使用时需要根据实际情况进行适当的调整和扩展。同时,要注意合理使用并发工具,避免产生死锁、竞态条件等问题,并保证线程安全性。
- 并发集合(使用ConcurrentHashMap类):
javaCopy codeimport java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ConcurrentHashMapExample {
private ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
public void put(String key, int value) {
map.put(key, value);
}
public int get(String key) {
return map.get(key);
}
}
- 并发队列(使用ConcurrentLinkedQueue类):
javaCopy codeimport java.util.concurrent.ConcurrentLinkedQueue;
public class ConcurrentQueueExample {
private ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
public void enqueue(String item) {
queue.add(item);
}
public String dequeue() {
return queue.poll();
}
}
以上是一些常见的并发集合和队列的使用示例,具体使用时需要根据实际需求进行适当调整和扩展。同时也要注意并发集合和队列的线程安全性,避免出现并发访问的问题。
- 点赞
- 收藏
- 关注作者
评论(0)