volatile和synchronized的区别,如何选择合适的同步方式?
在Java多线程编程中,volatile和synchronized是两个常用的关键字,用于保证共享变量的可见性和线程安全。虽然它们都可以用于实现线程安全,但是它们的运行机制和使用方式有很大不同。本文将详细介绍volatile和synchronized的区别以及如何选择合适的同步方式。
volatile关键字
定义和作用
Java中的volatile关键字用于修饰变量,表示该变量可能会被多个线程同时访问并修改,因此需要保证该变量的可见性。使用volatile修饰的变量,每次读取时都会从主内存中读取,每次修改时都会立刻写回主内存,而不会使用本地缓存。这样能够保证不同线程之间对该变量的修改是可见的。
示例代码
下面是一个简单的示例,使用volatile保证i变量的可见性:
public class VolatileDemo {
private volatile int i = 0;
public void inc() {
i++;
}
public static void main(String[] args) {
final VolatileDemo demo = new VolatileDemo();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
demo.inc();
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("i = " + demo.i);
}
}
在上面的代码中,volatile修饰了i变量,保证了不同线程对i变量修改的可见性,在main函数中输出i的值时,能够正确地得到10000。
synchronized关键字
定义和作用
Java中的synchronized关键字用于保证共享资源的互斥访问,即同一时间只有一个线程能够访问被synchronized修饰的代码块或方法。当某个线程获取到锁后,其他线程必须等待该线程释放锁之后才能继续执行。synchronized关键字可以用于保证线程安全,避免数据竞争和死锁等问题。
示例代码
下面是一个简单的示例,使用synchronized保证对i变量的操作是线程安全的:
public class SynchronizedDemo {
private int i = 0;
public synchronized void inc() {
i++;
}
public static void main(String[] args) {
final SynchronizedDemo demo = new SynchronizedDemo();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
demo.inc();
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("i = " + demo.i);
}
}
在上面的代码中,使用synchronized修饰了inc方法,保证了对i变量的操作是线程安全的,在main函数中输出i的值时,能够正确地得到10000。
volatile和synchronized的区别
从上面的示例可以看出,volatile和synchronized都可以保证多线程访问共享资源的安全性。然而,它们之间还是有很大的区别的。
并发性能比较
volatile只能保证可见性,不能保证原子性,因此在高并发场景下可能会存在数据竞争问题,导致数据出现错误。而synchronized关键字可以保证同一时间只有一个线程访问共享资源,避免了数据竞争和死锁等问题。因此,在性能要求较高的场景下,synchronized通常比volatile更适合。
可见性和原子性
volatile关键字主要是用于保证共享变量的可见性。当多个线程同时访问某个共享变量时,如果不加volatile关键字,则可能会出现多个线程同时修改该变量但是其中的某些线程没有看到其他线程对该变量的修改。加上volatile后,该变量的值将被及时更新到主内存中,使得所有线程都能看到最新的值。
synchronized关键字则不仅可以保证共享变量的可见性,还能够保证操作的原子性。当多个线程同时访问某个共享资源时,使用synchronized关键字可以保证同一时间只有一个线程访问该资源,避免了多个线程同时修改引起的数据竞争问题。这样能够保证对该资源的操作是原子性的。
使用场景
volatile关键字通常用于修饰状态标志、锁变量等轻量级变量,因为它的开销比较小,而且能够保证共享变量的可见性。而synchronized关键字则通常用于修饰临界区,即多个线程需要共同访问的代码块或方法,因为它能够保证对共享资源的操作是原子性的。
另外,由于使用synchronized关键字会涉及到锁的获取和释放,因此在高并发环境下有可能会造成性能瓶颈。为了避免这个问题,可以考虑使用Lock接口提供的Lock和Condition机制来实现同步,相比synchronized具有更高的灵活性和可扩展性。
综上所述,volatile和synchronized都是Java多线程编程中常用的同步机制,但是它们的运行机制和使用方式有很大不同,需要根据具体的应用场景选择合适的同步方式。
- 点赞
- 收藏
- 关注作者
评论(0)