ReentrantLock
ReentrantLock是Java并发编程中的一种锁机制。它的基本流程如下:
- 创建ReentrantLock对象。
- 在需要加锁的代码块前调用lock()方法,该方法会尝试获取锁,如果锁已被其他线程占用,则当前线程会被阻塞。
- 执行需要加锁的代码。
- 在加锁代码块的finally语句块中调用unlock()方法来释放锁。
ReentrantLock的特点和用法如下:
- 可重入性:ReentrantLock是可重入锁,即同一个线程可以重复获取该锁,而不会发生死锁。这是通过维护一个持有锁的线程的引用计数来实现的。
- 公平性:ReentrantLock可以指定是公平锁还是非公平锁,默认情况下是非公平锁。公平锁是按照线程申请锁的顺序来分配锁,而非公平锁则是随机分配锁,可能会导致某些线程饥饿。
- 条件变量:ReentrantLock提供了Condition接口的实现,可以通过该接口实现对线程的等待和唤醒机制,更灵活地控制线程的同步。
- 可中断:ReentrantLock提供了lockInterruptibly()方法,如果当前线程还没有获取到锁,但是被其他线程中断,可以通过该方法响应中断。
以下是一个使用ReentrantLock的Java代码示例:
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {// 创建并启动多个线程for (int i = 0; i < 5; i++) {Thread thread = new Thread(new MyThread());thread.start();}}static class MyThread implements Runnable {@Overridepublic void run() {try {// 加锁lock.lock();// 执行需要加锁的代码System.out.println("Thread " + Thread.currentThread().getId() + " is running");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放锁lock.unlock();}}}
}
在上述示例中,我们创建了一个ReentrantLock对象,并在MyThread的run()方法中加锁、执行代码、释放锁。在main方法中,我们创建并启动了5个线程,它们会依次获取锁并执行代码。由于ReentrantLock是可重入锁,同一个线程可以多次获取锁,所以每个线程都可以成功地执行代码块。
ReentrantReadWriteLock
ReentrantReadWriteLock是Java并发编程中的一个锁机制,它是一种读写锁,允许多个线程同时读共享资源,但只能有一个线程写资源。ReentrantReadWriteLock在实现上通过两个锁来实现,一个是读锁(共享锁),一个是写锁(独占锁)。
基本流程如下:
- 多个线程可以同时获取读锁,读锁之间不互斥,可以并发执行。
- 获取写锁的线程会阻塞其他线程的读锁和写锁,只有释放写锁后才允许其他线程获取读写锁。
ReentrantReadWriteLock的特点和用法:
- 公平性:可以选择公平模式或非公平模式,默认是非公平模式。在非公平模式下,允许锁被后来的线程插队,以提高吞吐量;在公平模式下,锁会按照请求的顺序分配给线程,保证公平性。
- 重入性:与ReentrantLock一样,ReentrantReadWriteLock可以重入,同一个线程可以多次获取读锁或写锁。
- 锁降级:一个线程拥有写锁的时候,可以先获取读锁,然后再释放写锁,这样就实现了锁的降级。
- 锁升级:读锁不能升级为写锁,因为会有死锁的风险。
下面是一个简单的示例代码,展示了ReentrantReadWriteLock的用法:
import java.util.concurrent.locks.ReentrantReadWriteLock;public class MyReadWriteLock {private int value = 0;private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();public int getValue() {lock.readLock().lock(); // 获取读锁try {return value;} finally {lock.readLock().unlock(); // 释放读锁}}public void increment() {lock.writeLock().lock(); // 获取写锁try {value++;} finally {lock.writeLock().unlock(); // 释放写锁}}
}
在上面的示例中,MyReadWriteLock类包含一个value变量和一个ReentrantReadWriteLock对象。getValue()方法获取读锁,读取value的值并返回。increment()方法获取写锁,将value加1。通过使用读写锁,多个线程可以同时读取value的值,但只有一个线程可以写入value的值。
Condition
Condition是Java并发编程中的一种同步机制,它可以用于实现线程之间的等待和通知。
基本流程:
- 创建一个Lock对象,通过Lock对象的newCondition()方法创建一个Condition对象。
- 通过Lock对象的lock()方法获取锁。
- 在某个线程中,通过Condition对象的await()方法使线程等待,同时释放锁。
- 在另一个线程中,通过Condition对象的signal()或signalAll()方法进行通知。
- 在第一个线程中,通过Condition对象的await()方法再次获取锁并继续执行。
特点和用法:
- 可以与Lock对象配合使用,对某个共享资源进行互斥访问和条件等待。
- 可以精确地控制线程的等待和通知。
示例代码:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ConditionExample {private final Lock lock = new ReentrantLock();private final Condition condition = lock.newCondition();private boolean flag = false;public void waitForFlag() throws InterruptedException {lock.lock();try {while (!flag) {condition.await(); // 线程等待并释放锁}} finally {lock.unlock();}System.out.println("Flag is true, continue executing.");}public void setFlag() {lock.lock();try {flag = true;condition.signalAll(); // 发送通知并唤醒等待线程} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {final ConditionExample example = new ConditionExample();Thread waitingThread = new Thread(() -> {try {example.waitForFlag();} catch (InterruptedException e) {e.printStackTrace();}});Thread settingThread = new Thread(() -> {try {Thread.sleep(2000); // 模拟执行耗时操作example.setFlag();} catch (InterruptedException e) {e.printStackTrace();}});waitingThread.start();settingThread.start();waitingThread.join();settingThread.join();}
}
在上述示例中,有两个线程,一个线程等待flag变量为true,另一个线程在某一时刻将flag设置为true。通过Condition对象的await()方法使等待线程进入等待状态,并释放锁,直到另一个线程通过Condition对象的signalAll()方法发送通知并唤醒等待线程,等待线程再次获取锁并继续执行。
总结
Java并发体系中的锁是用来管理多个线程对共享资源的访问的工具。锁的使用可以确保多个线程之间的同步和互斥,从而避免竞态条件和数据的不一致性。
Java中的锁可以分为两大类:内置锁和显式锁。
-
内置锁:
- synchronized关键字:synchronized是Java中最基本的内置锁机制。它可以修饰方法或代码块,一次只允许一个线程访问被修饰的代码块或方法。当一个线程获得锁时,其他线程必须等待锁释放才能继续执行。
- 锁对象:synchronized还可以用于指定一个对象作为锁。当一个线程获得该对象的锁时,其他线程对该对象的访问将被阻塞。这种方式可以实现更细粒度的锁控制。
-
显式锁:
- ReentrantLock类:ReentrantLock是Java提供的显式锁的实现类。它提供了与synchronized类似的功能,但提供了更灵活的锁控制。通过lock()方法获取锁,通过unlock()方法释放锁。ReentrantLock还提供了一些其他功能,如可中断锁、公平锁等。
- Condition接口:Condition接口是与显式锁ReentrantLock配合使用的重要组件。它可以让线程在等待某个条件满足时暂时释放锁,从而避免了线程一直占用锁资源而无法执行其他任务。
锁的选择应根据具体的需求和场景来决定。synchronized是最简单和常用的锁机制,适用于大部分情况。ReentrantLock提供了更多的灵活性和高级功能,例如可中断锁、公平锁等,但使用起来相对复杂一些。在多个线程需要等待某个条件满足时,使用Condition接口可以更好地控制线程的等待和唤醒。