在Java并发编程中,synchronized
关键字是一个用于实现线程安全的重要工具。它可以用来保护共享资源,确保在同一时刻只有一个线程可以访问该资源,从而避免数据不一致或其他并发问题。本文将深入探讨synchronized
的使用,包括其基本用法、特点及示例代码。
1. synchronized
的基本用法
synchronized
可以用于方法或代码块,主要有以下两种形式:
- 同步实例方法:通过在方法前加上
synchronized
关键字,来锁定当前对象(即this
)。 - 同步静态方法:通过在静态方法前加上
synchronized
关键字,使得这个方法在类级别上进行锁定,锁定的是该类的Class
对象。 - 同步代码块:可以在代码块中使用
synchronized
,指定需要锁定的对象。
2. 特点
- 互斥性:当一个线程获得锁之后,其他线程将被阻塞,直到持有锁的线程释放锁。
- 可重入性:同一个线程可以多次获得同一个锁,避免了死锁的产生。
- 可见性:
synchronized
保证了可见性,即当一个线程修改了共享变量的值,其他线程可以立即看到这个修改。
3. 示例代码
下面是一个示例,展示了如何使用synchronized
来保护共享资源。
public class SynchronizedExample {
private int counter = 0;
// 同步实例方法
public synchronized void increment() {
counter++;
}
// 获取 counter 的值
public int getCounter() {
return counter;
}
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.increment();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final counter value: " + example.getCounter());
}
}
4. 代码解析
在上面的代码示例中,我们创建了一个SynchronizedExample
类,其中包含一个 counter
变量和一个 increment
同步方法。我们启动了两个线程 t1
和 t2
,它们各自尝试对 counter
进行累加操作。
-
线程安全:由于
increment
方法被synchronized
修饰,这意味着在任意时刻,只会有一个线程能够执行这个方法,从而保证了counter
的安全性。 -
最终结果: 当两个线程都执行完毕后,我们会打印出
counter
的最终值,应该是2000,而不是一个小于2000的值,因为我们确保了每次累加操作都是安全的。
5. 结论
通过上述示例可以看出,synchronized
是实现线程安全的有效方式。虽然使用 synchronized
可以有效解决并发问题,但它也可能导致性能下降,尤其是在高并发情况下。因此,在实际应用中,需要根据具体的业务需求综合考虑选择合适的并发控制策略,如使用 ReentrantLock
或其他并发工具类。理解和正确使用 synchronized
是掌握 Java 并发编程的基础,对于构建可靠的多线程应用至关重要。