Java线程和共享资源
在Java编程中,线程是一种执行程序的方式,它可以同时运行多个线程,以实现并发执行的效果。当多个线程同时访问和修改共享资源时,就会发生线程安全问题。为了保证共享资源的正确访问和修改,我们需要使用同步机制来确保线程之间的互斥访问。
共享资源的问题
共享资源是指多个线程共同访问和修改的变量、对象或数据结构。当多个线程同时读写共享资源时,可能会出现以下问题:
- 竞态条件:多个线程对共享资源的访问顺序不确定,导致结果不同于预期。
- 数据不一致:多个线程同时修改共享资源,导致数据的状态不一致。
- 死锁:多个线程相互等待对方释放资源,导致程序无法继续执行。
为了解决这些问题,Java提供了多种同步机制,包括使用synchronized
关键字、使用锁(Lock)对象等。
使用synchronized关键字
synchronized
关键字是Java中最常用的同步机制,它可以用来修饰方法或代码块,从而实现对共享资源的同步访问。
同步方法
使用synchronized
关键字修饰方法可以确保同一时间只有一个线程执行该方法。示例代码如下:
public class Counter {
private int count;
public synchronized void increment() {
count++;
}
}
在上述代码中,increment
方法被声明为synchronized
,这意味着每次只能有一个线程执行该方法。当一个线程正在执行该方法时,其他线程将被阻塞,直到该线程执行完毕。
同步代码块
除了修饰方法外,还可以使用synchronized
关键字修饰代码块,以实现对共享资源的同步访问。示例代码如下:
public class Counter {
private int count;
private Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
}
在上述代码中,increment
方法中的代码块被synchronized
修饰,它使用了一个lock
对象作为同步锁。当一个线程获取到该锁时,其他线程将被阻塞,直到该线程释放锁。
使用锁对象
除了synchronized
关键字外,Java还提供了Lock
接口及其实现类,用于实现更复杂的同步需求。
使用ReentrantLock
ReentrantLock
是Lock
接口的一个实现类,它提供了更灵活的同步控制。示例代码如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
在上述代码中,lock
对象被用作同步锁,并通过lock
方法获取锁,通过unlock
方法释放锁。与synchronized
不同,Lock
接口提供了更灵活的锁获取和释放方式,可以更好地控制同步的粒度。
线程间通信
当多个线程共享资源时,有时需要线程之间进行协作,例如一个线程等待另一个线程完成某个操作后再继续执行。
Java提供了wait
、notify
和notifyAll
方法来实现线程间的通信。这些方法必须在synchronized
块内部调用,因为它们依赖于同步监视器。
wait和notify
wait
方法使当前线程等待,并释放持有的锁。notify
方法唤醒一个等待的线程。
示例代码如下:
public class SharedResource {
private int count;
private Object lock = new Object();
public void increment() throws InterruptedException {