Java并发编程-volatile关键字

1.volatile关键字

主要作用就是防止指令重排,以及保证变量的可见性.
在Java内存模型JMM中,Java虚拟机规范试图定义一种Java内存模型(JMM),来屏蔽掉各种硬件和操作系统的内存访问差异,让Java程序在各种平台上都能达到一致的内存访问效果。
在Java内存模型中,所有变量都存在主内存中,而每个线程又有自己的工作内存(CPU上的寄存器或高速缓存),所有的线程操作都以工作内存为主,并且只可以访问自己的工作内存,工作前后需要把值同步回主内存.这就造成了一个线程在自己的工作内存中修改变量的值,如果没有及时通知其他线程,会造成数据不一致的问题.
Volatile关键字保证了线程每次从主内存中读取数据而并不是从工作内存中.
在Java内存模型种,线程可以将变量保存在本地内存种
在这里插入图片描述

2.并发编程的三个重要特性

原子性:一个或多个操作,要么全部执行不会因任何因素的干扰而中断,要么全不执行,synchronized关键字保证了代码片段执行的原子性.
可见性:一个线程对共享变量值修改了之后,其他线程能立即看到修改后的值,volatile保证了变量的可见性.
有序性:Java代码在编译期间或运行期间因指令优化,执行顺序与编写顺序可能不一致,volatile禁止指令重排序,保证有序性.

3.synchronized与volatile关键字的区别

1.volatile是线程同步的轻量级实现,性能比synchronized要好,并且只能修饰变量,而synchronized是重要级实现,用来修饰方法和代码块
2.volatile保证数据可见性,不保证原子性,而synchronized都能保证.
3.volatile主要用于解决多线程中变量的可见性,而synchronized主要用于解决对对线程访问资源的同步性

4.谈谈 synchronized 和 ReentrantLock 的区别

(1)两者都是可重入锁
“可重入锁” 指的是线程可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增 1,所以要等到锁的计数器下降为 0 时才能释放锁。

(2)synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API
synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReentrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。

(3).ReentrantLock 比 synchronized 增加了一些高级功能
相比synchronized,ReentrantLock增加了一些高级功能。主要来说主要有三点:

等待可中断 : ReentrantLock提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly() 来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
可实现公平锁 : ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。
可实现选择性通知(锁可以绑定多个条件): synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition()方法。

Condition是 JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。
在使用notify()/notifyAll()方法进行通知时,被通知的线程是由 JVM选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知” ,这个功能非常重要,而且是 Condition接口默认提供的。而synchronized关键字就相当于整个 Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法只会唤醒注册在该Condition实例中的所有等待线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值