synchronized和volatile解决线程不安全的问题

1.synchronized

synchronized的底层是使用操作系统的mutex lock实现的。
当线程释放锁时,JMM会把该线程对应的工作内存中的共享变量刷新到主内存中
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内
存中读取共享变量
synchronized用的锁是存在Java对象头里的。
synchronized同步快对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;
同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入
Synchronized作用范围
作用实例方法时,锁住的是对象的实例(this)

作用静态方法时,锁住的是该类,该 Class所有实例,又因为 Class 的相关数据存储在永久带 PermGen(jdk1.8 则是 metaspace),永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程

线程A调用一个实例对象非静态Synchronized方法,允许线程B调用该实例对象所属类的静态s方法而不会发生互斥,前者锁的是当前实例对象,后者锁的是当前类
作用于同步代码块 锁住的当前对象,进入同步代码块前需要获得对象的锁
synchronized 关键字
它包括两种用法:synchronized 方法和 synchronized 块。   
synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:   
public synchronized void accessVal(int newVal);   
synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能
执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行
状态。  
synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:   synchronized(syncObject) {   //允许访问控制的代码   }    synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机
制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

2.volatile

1.由volatile修饰的共享变量,可保证其可见性和禁止指令重排序,建立内存屏障.
注意:volatile不能保证原子性;
a.当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;

b.在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

即执行到volatile变量时,其前面的所有语句都执行完,后面所有语句都未执行。且前面语句的结果对volatile变量及其后面语句可见。

2.volatile不适合复合操作 例如n++; 这个操作分为 ①读取n的值,②n的值加1③将修改后的值赋值给n;如下图: 结果并不等于20000;
为了保证线程安全,需要synchronized加锁.
在这里插入图片描述

3.单例模式下需要加volatile.
在这里插入图片描述

因为4 和5 有依赖关系,所以不会发生重排序,而6却可以重排序, 所以代码可能的执行顺序为 4 6 5 ,
那么, 在多线程情况下,线程A执行了4和6后 ,处于第一个if语句处的线程B执行1,那么线程B会指向一个地址,那么判断结果就不为空,所以会直接返回,而此时对象还没有初始化,就发生了错误.
而加了volatile后,就能保证456按顺序执行.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值