当多个线程同时访问共享资源时,可能会引发竞态条件(race condition)。竞态条件指的是多个线程在不正确的时间间隔内访问了共享数据,导致程序产生一些不可预料的结果。为了避免竞态条件,Java提供了同步机制,即保证在同一时间只有一个线程可以访问共享数据,从而保证程序执行结果的正确性。
Java中的同步可以使用synchronized关键字来实现,一个临界区域(也称为同步块)内只能有一个线程访问。synchronized可以用于方法,也可以用于代码块。通过synchronized实现同步时,还需要考虑到以下几点:
1.保证多个线程访问同一个临界区域
2.多个线程需要共享同一把锁
3.被锁住的对象应当是final类型的,为了避免发生指针的变更而导致不安全
下面是一个简单的Java同步示例,使用synchronized来控制多线程并发访问:
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public synchronized int getCount() {
return count;
}
}
在上面的示例代码中,利用synchronized关键字对count变量的三个方法进行了同步控制,以保证多线程并发访问时仅有一个线程可以访问。
下面再来介绍一下Java中的Wait与Notify机制,它们可以协同实现同步。
Wait用于暂停线程的执行,以等待其他线程通知自己继续执行。调用wait方法的线程在释放对象锁后会进入阻塞状态,直到其他线程调用同一对象的notify或notifyAll方法才能被唤醒。
Notify和NotifyAll用于唤醒处于阻塞状态的线程。notify方法只会唤醒正在等待中的一个线程,而notifyAll方法会唤醒所有处于等待中的线程。被唤醒的线程在重新获取对象锁后将调用wait方法之后的代码继续执行。
下面是一个简单的Java Wait与Notify示例:
public class WaitAndNotifyExample {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("Thread 1 waits for lock");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1 got notified");
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
System.out.println("Thread 2 got the lock");
lock.notify();
}
}
});
t1.start();
Thread.sleep(1000);
t2.start();
}
}
在上面的示例代码中,t1线程调用wait方法等待lock对象的解锁,t2线程获取了lock对象的锁之后,调用notify方法唤醒t1线程,t1线程继续执行并输出”Thread 1 got notified”。