synchronized
是 Java 中用于实现线程同步的关键字,它可以应用于方法或代码块上。它的作用是确保在同一时间只有一个线程可以执行被 synchronized 修饰的代码,从而避免多线程并发访问共享资源导致的数据不一致或冲突问题。
理解 synchronized 的关键概念是"互斥访问"和"可见性":
1.互斥访问:当一个线程获得了 synchronized 修饰的方法或代码块的锁,其他线程就无法进入该方法或代码块,必须等待当前线程释放锁才能执行。这确保了同一时间只有一个线程可以执行被 synchronized 修饰的代码,保证了数据的一致性。
2.可见性:synchronized 除了实现互斥访问外,还具有可见性的特性。当一个线程释放锁时(例如退出 synchronized 修饰的方法或代码块),它会将对共享变量的修改刷新到主内存,使得其他线程在获取锁时能够看到最新的值。这样可以避免线程之间读取过期的数据。
Synchronized 可以以不同的方式使用:
1.同步方法:可以使用 synchronized
修饰普通方法或静态方法。修饰普通方法时,默认的锁对象是该方法所属对象实例(即当前对象)。修饰静态方法时,默认的锁对象是该方法所属的 Class 对象。当一个线程访问该方法时,它会尝试获取锁对象,如果锁未被其他线程持有,则此线程获取锁并执行方法体,否则等待锁的释放。
2.同步代码块:可以使用 synchronized
修饰代码块。使用 synchronized 块时,需要指定一个锁对象,可以是任意对象。当一个线程进入 synchronized
块时,它会尝试获取锁对象,如果锁未被其他线程持有,则此线程获取锁并执行代码块,否则等待锁的释放。通常情况下,推荐使用 synchronized
块,因为它可以控制锁的粒度,减小锁的竞争范围,提高程序性能。
下面是一个使用 synchronized 的示例代码:
public class SynchronizedExample {
private int count;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.decrement();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Result: " + example.count);
}
}
在上面的示例中,increment()
和 decrement()
方法都被修饰为 synchronized
,确保每次只有一个线程可以执行这两个方法,从而保证了 count 的正确性。
需要注意的是,虽然 synchronized 能够确保线程安全性,但加锁也会引入一定的开销,并有可能导致线程的阻塞。因此,在使用 synchronized 时,需要根据实际情况进行合理的设计和权衡。
除了 synchronized,Java 还提供了其他的线程同步机制,如 Lock
、ReentrantLock
、ReadWriteLock
等,它们提供了更多灵活和高级的特性,可以满足不同的并发控制需求。