Java并发编程中的锁机制详解
在Java并发编程中,锁机制是保证线程安全的重要手段。锁的作用是确保同一时刻只有一个线程能够访问被锁保护的资源,从而避免数据不一致和并发冲突。本文将详细介绍Java并发编程中的锁机制,包括内置锁、重入锁、读写锁等,并通过代码示例讲解其使用方法和原理。
1. 内置锁(synchronized)
Java的内置锁是通过synchronized
关键字实现的,synchronized
可以用来修饰方法或者代码块,确保同一时刻只有一个线程能够执行被synchronized
保护的代码。
package cn.juwatech.lock;
public class SynchronizedDemo {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo demo = new SynchronizedDemo();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
demo.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + demo.getCount());
}
}
在上述代码中,increment
方法被synchronized
修饰,保证了count
变量的自增操作是线程安全的。
2. 重入锁(ReentrantLock)
ReentrantLock
是Java并发包中的可重入锁,它提供了比synchronized
更灵活的锁机制。例如,ReentrantLock
可以实现公平锁、非阻塞地获取锁等功能。
package cn.juwatech.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockDemo demo = new ReentrantLockDemo();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
demo.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + demo.getCount());
}
}
上述代码中,increment
方法使用ReentrantLock
进行加锁和解锁,确保count
变量的自增操作是线程安全的。
3. 读写锁(ReadWriteLock)
ReadWriteLock
是另一种常用的锁机制,它允许多个读线程并发地访问,但写线程是独占的。这在读多写少的场景中可以显著提高并发性能。
package cn.juwatech.lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
private int count = 0;
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
public void increment() {
rwLock.writeLock().lock();
try {
count++;
} finally {
rwLock.writeLock().unlock();
}
}
public int getCount() {
rwLock.readLock().lock();
try {
return count;
} finally {
rwLock.readLock().unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReadWriteLockDemo demo = new ReadWriteLockDemo();
Runnable writeTask = () -> {
for (int i = 0; i < 1000; i++) {
demo.increment();
}
};
Runnable readTask = () -> {
for (int i = 0; i < 1000; i++) {
System.out.println("Count: " + demo.getCount());
}
};
Thread t1 = new Thread(writeTask);
Thread t2 = new Thread(readTask);
Thread t3 = new Thread(readTask);
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
System.out.println("Final count: " + demo.getCount());
}
}
在上述代码中,increment
方法使用写锁进行加锁,getCount
方法使用读锁进行加锁,确保读写操作的线程安全。
4. 锁的条件变量
ReentrantLock
还提供了条件变量(Condition),可以实现更复杂的线程同步控制。通过Condition
对象,可以让线程在某个条件下等待,直到条件满足后再继续执行。
package cn.juwatech.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean ready = false;
public void waitForCondition() {
lock.lock();
try {
while (!ready) {
condition.await();
}
System.out.println("Condition met, proceeding...");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public void signalCondition() {
lock.lock();
try {
ready = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ConditionDemo demo = new ConditionDemo();
Runnable waitingTask = demo::waitForCondition;
Runnable signalingTask = demo::signalCondition;
Thread t1 = new Thread(waitingTask);
Thread t2 = new Thread(signalingTask);
t1.start();
Thread.sleep(1000);
t2.start();
t1.join();
t2.join();
}
}
在上述代码中,waitForCondition
方法在条件不满足时等待,直到signalCondition
方法将条件置为满足并通知所有等待线程。
5. StampedLock
StampedLock
是Java 8引入的一种锁机制,它提供了更高效的读写锁,并且支持乐观读操作。StampedLock
的主要特点是它使用了戳记(stamp)来控制锁的状态。
package cn.juwatech.lock;
import java.util.concurrent.locks.StampedLock;
public class StampedLockDemo {
private int count = 0;
private final StampedLock stampedLock = new StampedLock();
public void increment() {
long stamp = stampedLock.writeLock();
try {
count++;
} finally {
stampedLock.unlockWrite(stamp);
}
}
public int getCount() {
long stamp = stampedLock.tryOptimisticRead();
int currentCount = count;
if (!stampedLock.validate(stamp)) {
stamp = stampedLock.readLock();
try {
currentCount = count;
} finally {
stampedLock.unlockRead(stamp);
}
}
return currentCount;
}
public static void main(String[] args) throws InterruptedException {
StampedLockDemo demo = new StampedLockDemo();
Runnable writeTask = () -> {
for (int i = 0; i < 1000; i++) {
demo.increment();
}
};
Runnable readTask = () -> {
for (int i = 0; i < 1000; i++) {
System.out.println("Count: " + demo.getCount());
}
};
Thread t1 = new Thread(writeTask);
Thread t2 = new Thread(readTask);
Thread t3 = new Thread(readTask);
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
System.out.println("Final count: " + demo.getCount());
}
}
在上述代码中,increment
方法使用写锁进行加锁,getCount
方法尝试使用乐观读锁,如果失败则降级为悲观读锁,确保读写操作的线程安全。
总结
本文详细介绍了Java并发编程中的锁机制,包括内置锁、重入锁、读写锁、条件变量和StampedLock
。通过这些示例代码,开发者可以更好地理解和使用Java的各种锁机制,提高并发程序的性能和可靠性。