文章目录
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 标记的变量可以被编译器优化