volatile synchronized 和Lock的异同

##出现原因
当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。
因此引出多线程的三个问题:
1.原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
x = 10; //语句1
y = x; //语句2
x++; //语句3
x = x + 1; //语句4
  咋一看,有些朋友可能会说上面的4个语句中的操作都是原子性操作。其实只有语句1是原子性操作,其他三个语句都不是原子性操作。
  其他的不多解释,只要是语句2与语句1的不同在于要先去内存中取x的值.

2.可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
Java为可见性提供了volatile关键字,同时也可以用synchronized和Lock的加锁保证其可见性.
3.有序性:即进程按照程序的语序去执行.
利用这三个关键字都可以一定程度实现程序执行有序性,还有一个机制叫"happens-before".
##volatile关键字
1.双层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
可见性保证每一个线程每次读到都是最新的值,而不能保证其原子性.
public class Test {
public volatile int inc = 0;

public void increase() {
    inc++;
}
 
public static void main(String[] args) {
    final Test test = new Test();
    for(int i=0;i<10;i++){
        new Thread(){
            public void run() {
                for(int j=0;j<1000;j++)
                    test.increase();
            };
        }.start();
    }
     
    while(Thread.activeCount()>1)  //保证前面的线程都执行完
        Thread.yield();
    System.out.println(test.inc);
}

}
因此在此例中,返回值会小于10000.
它的禁止指令重排机制可以保证它后面(同时)没有数值需改的操作.
2.原理和机制
摘自《深入理解Java虚拟机》:
  “观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”
  lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:
  1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
  2)它会强制将对缓存的修改操作立即写入主存;
  3)如果是写操作,它会导致其他CPU中对应的缓存行无效。
3.使用场景
使用要求:
 1)对变量的写操作不依赖于当前值
 2)该变量没有包含在具有其他变量的不变式中
 如:状态标记量和double check.
 参考:https://www.cnblogs.com/dolphin0520/p/3920373.html
 ##synchronized关键字
 对于synchronized关键字就不做过多的解释,主要说说它的功能和缺陷.
 synchronized 的主要作用为:
1.确保线程互斥访问同步代码
2.共享变量变更能即时可见
3.有效解决重排序问题
synchronized 可修饰:普通方法,静态方法,对象。
缺陷:
一个线程获取资源锁的方式只有等待前一个线程执行完或者是jvm因线程异常而释放资源锁.因此在前一个线程I/O阻塞的情况下就会导致大量线程等待,因此它并不适合于多线程进行大量数据访问的环境中.
##Lock
解决synchronized关键缺陷的方式就是Lock锁,它可以获知线程是否获得锁.
Lock的缺陷在于它并不会去主动的释放锁,因此在释放异常时会造成死锁,使用时应该将其放到异常处理块中.

Lock lock = …;
lock.lock();
try{
//处理任务
}catch(Exception ex){

}finally{
lock.unlock(); //释放锁
}
##总结
在多线程问题中,volatile用于只要求可见性而不用原子性的问题中;synchronized用于少量线程的情况下,不适合用于大量线程和频繁的I/O操作的场景中;Lock 主要是解决了synchronized在大量线程和只读情况下的性能下降问题.
参考:https://www.cnblogs.com/handsomeye/p/5999362.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值