常见的锁类型及用法
- 1. synchronized锁
- 锁
- 3. ReadWriteLock(读写锁)
- 4. Condition锁
- 5. StampedLock锁
- 6. LockSupport锁
- 7. CountDownLatch锁
- 8.常见的面试题
在Java中,有多种类型的锁可用于线程同步和并发控制。
以下是Java中常见的锁类型的列表:
1. synchronized锁
- 特性:synchronized锁是Java中最基本的锁机制,它基于对象的内置监视器(或称为锁)来实现线程同步。它是一种独占锁,同一时刻只允许一个线程获取锁,并且其他线程将被阻塞等待。
- 使用示例:
public class SynchronizedExample { private int count = 0; private Object lock = new Object(); public void increment() { synchronized (lock) { count++; } } }
锁
- 特性:ReentrantLock是Java提供的可重入锁,它具有与synchronized锁相似的功能,但提供了更高的灵活性和扩展性。它允许线程重复获取同一把锁而不会造成死锁。
- 使用示例:
import 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(); } } }
3. ReadWriteLock(读写锁)
- 特性:ReadWriteLock是一个接口,它有两个实现类:ReentrantReadWriteLock和StampedLock。读写锁允许多个线程同时读取数据,但只允许一个线程写入数据。读锁是共享锁,写锁是独占锁。
- 使用示例:
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockExample { private int count = 0; private ReadWriteLock lock = new ReentrantReadWriteLock(); public void increment() { lock.writeLock().lock(); try { count++; } finally { lock.writeLock().unlock(); } } public int getCount() { lock.readLock().lock(); try { return count; } finally { lock.readLock().unlock(); } } }
4. Condition锁
- 特性:Condition锁是与锁相关联的条件对象。它允许线程在特定条件满足时等待或被唤醒。Condition锁通常与ReentrantLock结合使用,通过await()方法等待条件,通过signal()或signalAll()方法唤醒等待线程。
- 使用示例:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class ConditionExample { private int count = 0; private ReentrantLock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void increment() { lock.lock(); try { count++; condition.signalAll(); } finally { lock.unlock(); } } public void await() throws InterruptedException { lock.lock(); try { while (count == 0) { condition.await(); } // 执行其他操作 } finally { lock.unlock(); } } }
5. StampedLock锁
- 特性:StampedLock是Java 8中引入的一种乐观锁机制。它提供了一种读写锁的变体,允许乐观读取操作,从而提供更高的并发性能。StampedLock通过返回一个标记(stamp)来标识锁的状态。
- 使用示例:
import java.util.concurrent.locks.StampedLock; public class StampedLockExample { private int x = 0; private int y = 0; private StampedLock lock = new StampedLock(); public void writeData(int newX, int newY) { long stamp = lock.writeLock(); try { x = newX; y = newY; } finally { lock.unlockWrite(stamp); } } public double readDistanceFromOrigin() { long stamp = lock.tryOptimisticRead(); int currentX = x; int currentY = y; if (!lock.validate(stamp)) { stamp = lock.readLock(); try { currentX = x; currentY = y; } finally { lock.unlockRead(stamp); } } return Math.sqrt(currentX * currentX + currentY * currentY); } }
6. LockSupport锁
- 特性:LockSupport是一个线程阻塞工具,可以用于创建锁和其他同步类的基本线程阻塞原语。它与每个线程都关联了一个许可证,可以用于阻塞和解除阻塞线程。
- 使用示例:
import java.util.concurrent.locks.LockSupport; public class LockSupportExample { public void doSomething() { // 执行前置操作 LockSupport.park(); // 阻塞当前线程 // 执行后置操作 } public void wakeupThread() { // 唤醒阻塞的线程 LockSupport.unpark(thread); } }
7. CountDownLatch锁
- 特性:CountDownLatch是一个同步辅助类,它允许一个或多个线程等待其他线程完成操作后再继续执行。它通过一个计数器实现,计数器的初始值为线程数,每个线程完成任务后,计数器减1。
- 使用示例:
import java.util.concurrent.CountDownLatch; public class CountDownLatchExample { private CountDownLatch latch = new CountDownLatch(3); public void doSomething() throws InterruptedException { // 执行前置操作 latch.countDown(); // 完成任务,计数器减1 latch.await(); // 等待其他线程完成任务 // 执行后置操作 } }
8.常见的面试题
在Java中,锁是多线程编程中的重要概念,常常成为面试中的热门话题。
以下是一些常见的Java锁相关的面试题以及它们的详细解析:
-
什么是可重入锁?Java中提供了哪些可重入锁的实现?
- 解析:可重入锁是指线程可以重复获取已经持有的锁而不会导致死锁。在Java中,ReentrantLock和synchronized都是可重入锁的实现。它们允许线程在持有锁的情况下再次获取同一把锁。
-
synchronized和ReentrantLock之间有什么区别?
- 解析:synchronized是Java中的关键字,而ReentrantLock是包中的类。主要区别如下:
- 可重入性:synchronized是可重入的,而ReentrantLock也是可重入的。
- 锁的获取方式:synchronized锁是隐式获取和释放的,而ReentrantLock需要显式调用lock()和unlock()方法来获取和释放锁。
- 等待可中断:ReentrantLock提供了一个可以响应中断的锁获取方式,可以在等待锁的过程中中断线程。
- 解析:synchronized是Java中的关键字,而ReentrantLock是包中的类。主要区别如下:
-
什么是读写锁?读写锁的作用是什么?
- 解析:读写锁是一种特殊类型的锁,允许多个线程同时读取数据,但只允许一个线程写入数据。读写锁的主要作用是提高读操作的并发性能,适用于读多写少的场景。Java中的读写锁由接口ReadWriteLock定义,主要实现类是ReentrantReadWriteLock。
-
Condition是什么?它在锁中的作用是什么?
- 解析:Condition是与锁相关联的条件对象,用于实现线程间的等待/通知机制。它提供了await()、signal()和signalAll()等方法。在锁中,Condition的作用是允许线程在某个条件满足时等待,或者在满足条件时通知等待的线程继续执行。
-
什么是乐观锁和悲观锁?StampedLock是哪种类型的锁?
- 解析:乐观锁和悲观锁是并发控制中的两种不同策略。悲观锁假设会发生并发冲突,因此每次操作都会获取锁,阻止其他线程访问。乐观锁则假设不会发生并发冲突,通过版本号或标记来判断操作是否成功。StampedLock是乐观锁的一种实现,它允许多个线程并发读取数据,但在写操作时需要获取独占锁。
-
CountDownLatch和CyclicBarrier有什么区别?
- 解析:CountDownLatch和CyclicBarrier都是线程同步的辅助类,但有以下区别:
- 计数器功能:CountDownLatch通过一个计数器实现,计数器的初始值为线程数,每个线程完成任务后计数器减1。CyclicBarrier通过等待一组线程达到某个屏障点来触发执行。
- 重用性:CountDownLatch的计数器减到0后无法重用,而CyclicBarrier可以被重用。
- 等待机制:CountDownLatch的线程等待通过await()方法实现,而CyclicBarrier的线程等待通过await()方法和屏障点的计数器实现。
- 解析:CountDownLatch和CyclicBarrier都是线程同步的辅助类,但有以下区别:
这些是Java中常见的锁相关的面试题以及对它们的详细解析。理解这些概念和特性对于处理多线程编程和并发控制非常重要。