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 的区别?

volatilesynchronized
修饰只能用于修饰变量可以用于修饰方法、代码块
线程阻塞不会发生线程阻塞会发生阻塞
原子性不能保证变量的原子性可以保证变量原子性
可见性可以保证变量在线程之间访问资源的可见性可以间接保证可见性,因为它会将私有内存中和公共内存中的数据做同步
同步性能保证变量在私有内存和主内存间的同步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 标记的变量可以被编译器优化
  • 8
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值