Java 中 Synchronized 的用法解析
简介
Java 中的 Synchronized 关键字是用来在多个线程之间协调访问共享资源的一种机制。通过 Synchronized,我们可以保证同一时刻只有一个线程访问被保护的代码块(或方法),防止出现数据竞争等问题。在本文中,我们将深入探讨 Synchronized 的用法,包括 Synchronized 关键字的使用方法、Synchronized 的作用范围、Synchronized 的性能问题以及优化 Synchronized 性能的方法。
Synchronized 关键字的使用方法
Synchronized 关键字可以用在两种不同的场景中:同步方法和同步代码块。
同步方法
使用 Synchronized 关键字来修饰方法,表示该方法是同步方法。在同步方法中,Synchronized 锁定的是该方法所属的对象,在该对象上只有一个线程可以执行同步方法。
示例代码如下:
public class Test {
private int count;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
在上述代码中,increment 和 getCount 方法都被修饰为 synchronized,表示它们是同步方法。由于它们锁定的是该对象,因此同一时刻只能有一个线程访问该对象的 increment 或 getCount 方法。如果有多个线程同时调用 increment 或 getCount 方法,其中只有一个线程能够执行,其他线程需要等待。
同步代码块
我们也可以使用 Synchronized 关键字来修饰代码块,在需要进行同步的代码块中加入 Synchronized 块,用来锁定指定的对象。在同步代码块中,Synchronized 锁定的是指定的对象,该对象上只有一个线程可以执行同步代码块。
示例代码如下:
public class Test {
private int count;
public void increment() {
synchronized (this) {
count++;
}
}
public int getCount() {
synchronized (this) {
return count;
}
}
}
在上述代码中,increment 和 getCount 方法中的 Synchronized 同步代码块都使用的是 this 对象进行锁定,表示该代码块在该对象上锁定,只能有一个线程访问该对象中的同步代码块。
Synchronized 的作用范围
上述代码中我们所使用的 Synchronized 关键字都是对整个方法或代码块进行锁定,但是实际上 Synchronized 的粒度可以更细。
对象锁
当我们使用 Synchronized 同步方法或同步代码块时,Synchronized 会锁定调用该方法或执行该代码块所在的对象。因此,使用 Synchronized 必须确认所锁定的对象是唯一的,否则可能会出现多个线程同时访问同一对象的问题。
类锁
在 Java 中,每个 Class 对象都在 JVM 运行时中唯一存在,因此我们也可以使用 Synchronized 锁定 Class 类型的对象。在锁定类对象时,表示对该类中的所有对象的访问进行同步。
示例代码如下:
public class Test {
private static int count;
public static synchronized void increment() {
count++;
}
public static synchronized int getCount() {
return count;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count: " + getCount());
}
}
在该示例代码中,increment 和 getCount 方法都被定义为 static,并且使用 static synchronized 关键字进行修饰。在该类中,Synchronized 会锁定该类的 Class 对象,因此同时只能有一个线程访问 increment 或 getCount 方法。
Synchronized 的性能问题
尽管 Synchronized 是实现线程之间同步访问的一种有效方式,但是在某些情况下,Synchronized 可能会成为性能的瓶颈,导致程序执行速度变慢。
当使用 Synchronized 时,每个线程需要在进入同步代码块时获得锁,并在退出同步代码块时释放锁。因此,如果 synchronized 的粒度比较大,例如 Synchronized 修饰的是整个方法,那么将会有大量的线程在等待获取锁,从而导致程序执行速度变慢。
优化 Synchronized 性能的方法
为了避免 Synchronized 在性能上造成的影响,我们可以采用以下几种方式优化 Synchronized 的性能。
减小锁的粒度
对于一个对象中的多个方法,不要都使用 Synchronized 进行修饰,可以减小锁的粒度,使得每个线程在获取锁时只需要等待锁的粒度更小的区域。
使用局部变量代替成员变量
在使用 Synchronized 时,如果使用的对象是成员变量,那么每个线程在进入同步代码块时都需要获取该对象的锁。因此,可以将该成员变量转换为局部变量,这样每个线程只需要获取该局部变量的锁,可以避免等待其他线程对该成员变量加锁的时间。
总结
在 Java 中,Synchronized 是一种非常常见的线程同步机制,可以确保多线程之间访问共享数据时的安全性。在使用 Synchronized 时,需要注意锁的范围,避免出现数据竞争等问题。同时,在使用 Synchronized 时也需要注意性能问题,建议尽量减小锁的粒度,以提高程序的执行效率。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中Synchronized的用法解析 - Python技术站