volatile 与 synchronize 详解


Java支持多个线程同时访问一个对象或者对象的成员变量,由于每个线程可以拥有这个变量的拷贝(虽然对象以及成员变量分配的内存是在共享内存中的,但是每个执行的线程还是可以拥有一份拷贝,这样做的目的是加速程序的执行,这是现代多核处理器的一个显著特性),所以程序在执行过程中,一个线程看到的变量并不一定是最新的。

volatile

volatile的应用

在多线程并发编程中 synchronized 和 volatile 都扮演着重要的角色,volatile 是一个轻量级的 synchronized ,它在多处理器开发中保证了共享变量的 可见性。可见性的意思是当一个线程修改一个共享变量的时候,另一个线程能读取到这个共享变量被修改后的值。如果 volatile 使用恰当的话,它比 synchronized 的使用和执行成本更低,因为 volatile 不会引起线程上下文的切换和调度

volatile的定义与实现原理

Java 编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致性地更新,线程应该确保通过排他锁来单独获取这个变量。Java 提供的 volatile 在某些情况下比锁要方便。如果一个字段被声明为 volatile,那么Java模型确保所有的线程看到这个变量的值是一致的

作用:
volatile关键字用作成员变量的修饰符,以强制单个线程每次从共享内存中重新读取变量的值
访问。 此外,各个线程被迫将更改写回到共享记忆一旦发生。 这样,两个不同的线程总是看到相同的在任何特定时间成员变量的值

volatile的两条实现原则

  • Lock 前缀指令会引起处理器缓存回写到内存
  • 一个处理器的缓存回写到内存会导致其他的处理器的缓存无效

synchronized

synchronized 关键字可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性

synchronized的实现原理与应用

在多线程并发编程中 synchronized 一直是元老级的角色,很多人都会直呼它为重量级锁。但是,随着 Java SE 1.6 对 synchronized 进行了各种优化之后,有些情况下synchronized 并没有那么重了

synchronized如何实现同步?

synchronized 实现同步的基础:Java中的每一个对象都可以作为锁。具体的表现形式有以下三种:

  • 对于普通同步方法,锁是当前实例对象
  • 对于静态同步方法,锁是当前类的Class对象
  • 对于同步方法块,锁是 synchronized 括号里配置的对象

当一个线程试图访问同步代码块时,必须先获取到锁,退出或抛出异常时必须释放锁

volatile 与 synchronized 的区别?

volatile synchronized
修饰 只能用于修饰变量 可以用于修饰方法、代码块
线程阻塞 不会发生线程阻塞 会发生阻塞
原子性 不能保证变量的原子性 可以保证变量原子性
可见性 可以保证变量在线程之间访问资源的可见性 可以间接保证可见性,因为它会将私有内存中和公共内存中的数据做同步
同步性 能保证变量在私有内存和主内存间的同步 synchronize是多线程之间访问资源的同步性
  • volatile 是线程同步的轻量级实现,所以 volatile 的性能要比 synchronize 好;随着jdk技术的发展,synchronize 在执行效率上会得到较大提升,所以 synchronize 在项目过程中还是较为常见的
  • 对于 volatile 修饰的变量,可以解决变量读时可见性问题,无法保证原子性。对于多线程访问同一个实例变量还是需要加锁同步

在多线程定义中,volatile 关键字主要是在属性上使用的,表示此属性为直接数据操作,而不进行副本的拷贝处理。这样的话在一些书上就将其错误的理解为同步属性了。
在这里插入图片描述

package com.java.springtest.test;

/**
 * @author Woo_home
 * @create by 2020/1/20
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception{
        ThreadShop shopA = new ThreadShop();
        ThreadShop shopB = new ThreadShop();
        ThreadShop shopC = new ThreadShop();
        new Thread(shopA,"A 店铺").start();
        new Thread(shopB,"B 店铺").start();
        new Thread(shopC,"C 店铺").start();
    }
}

class ThreadShop implements Runnable {

    private volatile int product = 5; // 直接内存操作

    @Override
    public void run() {
        synchronized (this) {
            while (this.product > 0) {
                try {
                    Thread.sleep(100);
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "商品处理,product = " + this.product--);
            }
        }
    }
}

在这里插入图片描述

面试题: 请解释 volatile 与 synchronized 的区别?

  • volatile 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  • volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别
  • volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性
  • volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
  • volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化
发布了192 篇原创文章 · 获赞 902 · 访问量 15万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览