在Java中,加锁(Locking)是一种同步机制,用于控制对共享资源的并发访问,以防止多个线程同时修改数据,从而避免数据不一致和并发问题。Java提供了多种加锁的方法,包括使用synchronized
关键字和java.util.concurrent.locks
包中的类。
使用synchronized
关键字
synchronized
关键字可以用来修饰方法或者代码块,以实现加锁。当一个线程访问一个对象的synchronized
方法或者代码块时,它会获得该对象的锁。其他线程对该对象的synchronized
方法或代码块的访问将会被阻塞,直到锁被释放。
示例:使用synchronized
修饰方法
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
在这个例子中,increment
和getCount
方法都被synchronized
修饰,确保了同时只有一个线程能够修改count
变量。
示例:使用synchronized
修饰代码块
public class Counter {
private int count = 0;
public void increment() {
synchronized(this) {
count++;
}
}
public int getCount() {
synchronized(this) {
return count;
}
}
}
在这个例子中,我们使用synchronized
修饰了一个代码块,并且锁对象是this
。这样可以确保同一时间只有一个线程能够执行increment
或getCount
方法中的代码块。
使用java.util.concurrent.locks
包
java.util.concurrent.locks
包提供了更高级的锁定机制,允许更灵活地实现多线程同步。Lock
接口是这个包中的核心接口,它提供了比synchronized
更复杂的锁定行为,例如尝试非阻塞获取锁、可中断的锁获取、公平性等。
示例:使用ReentrantLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
在这个例子中,我们使用ReentrantLock
来代替synchronized
。lock()
方法用于获取锁,unlock()
方法用于释放锁。注意,我们使用try-finally
块来确保锁总是被释放,即使在increment
方法中发生异常也是如此。
使用java.util.concurrent.locks
包中的锁机制可以提供更细粒度的控制,但同时也需要更谨慎的编程,以避免死锁和资源泄露等问题。