目录
引言
在 Java 编程里,多线程能够显著提升程序的性能与响应速度。不过,多个线程同时访问共享资源时,可能会引发数据不一致、竞态条件等问题。为了确保线程安全,Java 提供了多种多线程同步机制,其中锁和信号量是较为常用的两种。接下来,我们会深入探讨这两种机制。
锁机制
基本概念
锁是一种用于控制多个线程对共享资源访问的同步机制。当一个线程获取到锁之后,其他线程就必须等待该线程释放锁,才能继续访问共享资源。这样可以保证同一时刻只有一个线程能访问共享资源,避免数据不一致的问题。
内置锁(synchronized 关键字)
- 用法:
synchronized
关键字可以修饰方法或者代码块。当修饰方法时,该方法在同一时刻只能被一个线程访问;当修饰代码块时,需要指定一个锁对象,只有获取到该锁对象的线程才能执行代码块中的内容。 - 示例代码:
public class SynchronizedExample {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void decrement() {
synchronized (this) {
count--;
}
}
}
- 原理:每个 Java 对象都有一个与之关联的监视器(monitor),
synchronized
方法或者代码块在进入时会尝试获取对象的监视器,获取成功才能执行,执行完毕后释放监视器。
重入锁(ReentrantLock)
- 用法:
ReentrantLock
是 Java 提供的一个可重入的互斥锁,它比synchronized
关键字更加灵活。可以通过lock()
方法获取锁,通过unlock()
方法释放锁。 - 示例代码:
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();
}
}
}
- 特点:可重入意味着同一个线程可以多次获取同一把锁而不会被阻塞;还支持公平锁模式,即按照线程请求锁的顺序来分配锁。
信号量机制
基本概念
信号量是一种更高级的同步机制,它可以控制同时访问共享资源的线程数量。信号量内部维护了一个许可(permit)计数器,线程在访问共享资源之前需要先获取许可,访问结束后释放许可。
Java 中的信号量(Semaphore)
- 用法:
Semaphore
类位于java.util.concurrent
包中,通过构造函数指定初始的许可数量。线程可以通过acquire()
方法获取许可,通过release()
方法释放许可。 - 示例代码:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private static Semaphore semaphore = new Semaphore(2);
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 获取到许可,开始访问资源");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " 访问资源结束,释放许可");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
- 应用场景:适用于需要限制并发访问数量的场景,比如数据库连接池,通过信号量可以限制同时获取数据库连接的线程数量。
锁与信号量的比较
功能侧重点
- 锁:主要用于实现互斥访问,确保同一时刻只有一个线程能访问共享资源,侧重于保护数据的一致性。
- 信号量:更侧重于控制并发访问的线程数量,可以允许多个线程同时访问共享资源,但数量是有限制的。
使用场景
- 锁:适用于对共享资源进行独占访问的场景,例如对全局变量的修改、对文件的读写操作等。
- 信号量:适用于需要限制并发访问数量的场景,如资源池的管理、限流等。
总结
锁和信号量都是 Java 中重要的多线程同步机制,它们各自有不同的特点和适用场景。在实际开发中,需要根据具体的需求来选择合适的同步机制,以确保程序的线程安全和性能。