在 Java 多线程编程中,锁机制是一种重要的同步工具,用于协调线程之间的访问和操作。Java 中的锁可以分为几类,主要包括:
1. 互斥锁(Mutex)
- 互斥锁是最常见的一种锁,用于保护共享资源,确保同一时刻只有一个线程可以访问这个资源。
- Java 中的 `synchronized` 关键字就是一种互斥锁机制,通过对代码块或方法进行同步,确保在同一时刻只有一个线程可以执行。
class SharedResource {
private int count = 0;
// 使用 synchronized 关键字创建互斥锁
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
class MyThread extends Thread {
private SharedResource sharedResource;
public MyThread(SharedResource sharedResource) {
this.sharedResource = sharedResource;
}
@Override
public void run() {
// 在互斥锁的保护下对共享资源进行操作
for (int i = 0; i < 10000; i++) {
sharedResource.increment();
}
}
}
public class MutexLockExample {
public static void main(String[] args) throws InterruptedException {
SharedResource sharedResource = new SharedResource();
// 创建两个线程共享同一个对象
Thread thread1 = new MyThread(sharedResource);
Thread thread2 = new MyThread(sharedResource);
// 启动线程
thread1.start();
thread2.start();
// 等待两个线程执行完成
thread1.join();
thread2.join();
// 输出最终的共享资源的值
System.out.println("Final Count: " + sharedResource.getCount());
}
}
2. 可重入锁(Reentrant Lock)
- 可重入锁是一种允许同一个线程多次获得同一把锁的锁机制。如果一个线程已经获得了锁,它可以再次获取相同的锁而不被阻塞。
- Java 中的 `ReentrantLock` 类提供了可重入锁的实现。
import java.util.concurrent.locks.ReentrantLock;
class SharedResource {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
public void performTask() {
lock.lock(); // 获取锁
try {
// 可重入锁允许在持有锁的情况下再次获取锁
anotherTask();
count++;
System.out.println(Thread.currentThread().getName() + ": Count is " + count);
} finally {
lock.unlock(); // 释放锁
}
}
private void anotherTask() {
lock.lock(); // 在同一个线程中再次获取锁
try {
count++;
System.out.println(Thread.currentThread().getName() + ": Another Task, Count is " + count);
} finally {
lock.unlock(); // 释放锁
}
}
}
class MyThread extends Thread {
private SharedResource sharedResource;
public MyThread(SharedResource sharedResource) {
this.sharedResource = sharedResource;
}
@Override
public void run() {
sharedResource.performTask();
}
}
public class ReentrantLockExample {
public static void main(String[] args) {
SharedResource sharedResource = new SharedResource();
// 创建两个线程共享同一个对象
Thread thread1 = new MyThread(sharedResource);
Thread thread2 = new MyThread(sharedResource);
// 启动线程
thread1.start();
thread2.start();
}
}
3. 读写锁(Read/Write Lock)
- 读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。这样可以提高读操作的并发性。
- Java 中的 `ReentrantReadWriteLock` 类提供了读写锁的实现。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWriteLockExample {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private int sharedData = 0;
public int readData() {
readWriteLock.readLock().lock(); // 获取读锁
try {
System.out.println(Thread.currentThread().getName() + " is reading data: " + sharedData);
return sharedData;
} finally {
readWriteLock.readLock().unlock(); // 释放读锁
}
}
public void writeData(int newValue) {
readWriteLock.writeLock().lock(); // 获取写锁
try {
System.out.println(Thread.currentThread().getName() + " is writing data: " + newValue);
sharedData = newValue;
} finally {
readWriteLock.writeLock().unlock(); // 释放写锁
}
}
public static void main(String[] args) {
ReentrantReadWriteLockExample example = new ReentrantReadWriteLockExample();
// 创建并启动多个读线程和一个写线程
for (int i = 0; i < 5; i++) {
new ReaderThread(example).start();
}
new WriterThread(example, 100).start();
}
static class ReaderThread extends Thread {
private final ReentrantReadWriteLockExample example;
public ReaderThread(ReentrantReadWriteLockExample example) {
this.example = example;
}
@Override
public void run() {
example.readData();
}
}
static class WriterThread extends Thread {
private final ReentrantReadWriteLockExample example;
private final int newValue;
public WriterThread(ReentrantReadWriteLockExample example, int newValue) {
this.example = example;
this.newValue = newValue;
}
@Override
public void run() {
example.writeData(newValue);
}
}
}
4. 悲观锁和乐观锁
- 悲观锁认为在整个过程中都会发生冲突,因此在访问共享资源前会先加锁。`synchronized` 和 `ReentrantLock` 就是悲观锁的实现。
- 乐观锁则认为冲突是少数情况,它不会在访问共享资源前加锁,而是在更新时检查是否有其他线程修改了数据。常见的实现方式包括无锁编程、CAS(Compare and Swap)等。
5. 自旋锁
- 自旋锁是一种在获取锁失败时,线程不会立即被阻塞,而是采用循环方式不断尝试获取锁的锁机制。适用于锁占用时间很短暂的情况。
- Java 中的 `java.util.concurrent.atomic` 包中的原子类使用了类似的自旋锁的概念。
import java.util.concurrent.atomic.AtomicInteger;
public class SpinLockExample {
private AtomicInteger lock = new AtomicInteger(0);
public void lock() {
while (!tryLock()) {
// 自旋等待锁
}
}
public void unlock() {
lock.set(0);
}
private boolean tryLock() {
return lock.compareAndSet(0, 1);
}
public static void main(String[] args) {
SpinLockExample spinLockExample = new SpinLockExample();
// 创建并启动两个线程
new WorkerThread(spinLockExample, "Thread-1").start();
new WorkerThread(spinLockExample, "Thread-2").start();
}
static class WorkerThread extends Thread {
private final SpinLockExample spinLockExample;
public WorkerThread(SpinLockExample spinLockExample, String name) {
super(name);
this.spinLockExample = spinLockExample;
}
@Override
public void run() {
spinLockExample.lock();
try {
System.out.println(getName() + " is working");
// 模拟工作
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
spinLockExample.unlock();
System.out.println(getName() + " is done working");
}
}
}
}
6. 公平锁和非公平锁
- 锁会按照线程请求锁的顺序来分配锁,而非公平锁则不考虑线程请求锁的顺序,有可能导致某些线程一直获取不到锁。
- `ReentrantLock` 可以通过构造函数指定是公平锁还是非公平锁。
7. 条件锁(Condition)
-
条件锁是一种支持线程之间按照条件等待和通知的锁机制。它通常与可重入锁一起使用。
-
Java 中的 `ReentrantLock` 类提供了 `newCondition` 方法用于创建条件锁。
这些锁机制在不同的场景下有各自的优缺点,选择合适的锁对于多线程程序的性能和正确性都有着重要的影响。
(记不住呀/(ㄒoㄒ)/~~,老是忘记,搞混)