想要解决线程安全问题,首先要知道为什么会造成线程不安全?
在单线程中,我们从来没有提到个线程安全问题,线程安全问题是只出现在多线程中的一个问题。因为多线程情况下有共享数据,每个线程都共享这些数据并对这些数据进行修改,当需要修改数据进行写入时,还有线程调度的交叉执行,还有内存模型的原因引起了不安全问题。
要保证线程安全是一件很难的事,一般我们着眼于以下三个方面:
(三个核心)
1.原子性
2.可见性
3.重排序问题
首先来解释一下,什么是原子性,可见性,重排序问题?
原子性,一段不可再分的代码片段,要么全部执行要么都不执行。(一句代码不一定就是原子的,例如a=b,将b从主内存中加载到工作内存,再将b的值赋给a,最后将a保存到主内存中,很明显是三步。)
可见性,所有的数据存在主内存中,每个线程有自己的工作内存,工作时将数据从主内存中加载到工作内存(这样做有利于提高运行效率),这时候,如果本线程将数据做了修改,其他线程及时的看到了,这就叫可见的(但往往本线程已经修改了数据,在其他线程中还是用的是旧的数据,这就发生了错误,就是不安全问题。)
重排序问题,在单线程中我们的都是按照顺序执行的,但是在多线程中,CPU/编译器/JIT会对代码运行顺序进行一定的调整(按照更加合适的方式执行,优化),但是这样就没办法保证结果的正确性,会出错,这就是代码重排序问题。
为了解决这些不安全问题而产生了synchronized和volatile关键字
接下了我们看看synchronized和volatile是如何保证安全性的,以及学会使用方法,来保证我们自己写的代码的线程安全。
1.synchronized
先学习synchronized 的语法:
1)方法修饰符——和其他修饰符的使用类似
2)代码块——作为代码块出现
接下来,谈谈synchronized 是如何保证线程安全的?
synchronized 是通过给对象加锁,保证了原子性和可见性来使线程安全的,
先看给普通方法加锁,
锁是加在在堆里创建的对象上面,当一个线程抢占CPU成功时,检查该对象,发现锁是开的(锁初始状态下是开的状态),就给这个对象加锁,直到方法执行结束(正常/异常),释放锁。在当前线程加锁运行代码的过程中,不一定可以一直抢占CPU,如果自己的时间片用完了或者主动放弃CPU,线程调度到其他线程,而其他线程这时抢占CPU成功了,这时检查该对象(注意:和前面是同一对象,当前对象,this指向的那个对象)发现已经上锁,这时该线程直接放入阻塞队列,只有当这把锁被释放后,才能重新进入就绪队列,重新获得了抢占CPU的机会,每一把锁都有一个对应的阻塞队列。
再看给静态方法加锁,
给静态方法加的锁,是在放类的方法区里,加在类的元对象上面。
synchronized的代码表现:
| 表现 ---------- | 锁的对象 ------- | 何时加锁---- | 何时释放 - |
|修饰普通方法 | this -------------- | 进入方法---- | 退出方法 - |
|修饰静态方法 | 类名.class ----- | 进入方法 — | 退出方法-- |
| 代码块-------- |小括号引用指向 | 进入代码块 | 退出代码块 |
加锁保证了原子性,并在一定程度上保证了可见性。
在加锁时,所有线程会同步当前的数据,在释放锁时,数据又会同步一次,但是在加锁-解锁这个代码运行的过程中,没有保证可见性。
加锁内部的代码可以进行重排序,外部的代码也可以进行重排序,但是内部和外部之间是不相通的不可以交换代码执行顺序。
使用synchronized的缺点:理论上所有的线程安全问题都可以使用synchronized来解决,但是成本非常高。(不断的进行线程调度本身就会花费很多时间,而且,如果每次都是同一线程抢占CPU成功,其他线程就会一直处于阻塞-抢占的过程中)
在使用synchronized关键字时加锁时,要尽量把它放在合适的位置上,如果范围过大,将没有意义,即关注锁的粒度问题,粗粒度,细粒度。
2.volatile
语法:修饰变量
可以保证该变量的可见性问题(不再赘述)
可以部分保证代码的重排序问题(对象初始化,分为三步:new 、 对象初始化、对象赋值给引用。volatile可以保证这三步是顺序执行的)
关于原子性的一个总结:
基本数据类型里,
byte、int、Boolean、float、char、short作为字面量时是原子的,作为变量时不是原子的;
long、double在任何时候都不是原子的。(因为它们都是64位的在32位机运行时,分为低32位和高32位,不是一步执行的)
最后再提一点:单例模型:一个类只会生成一个对象(只有一个实例),大家共用的是同一个实例)
————————————————
版权声明:本文为CSDN博主「小矮多」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/nefss_/article/details/102989716