文章目录
线程安全
由于多个线程之间是抢占式执行的
, 这就给调度执行顺序带来了随机性, 程序员需要在n多个调度执行顺序中保证每一个顺序最后执行的结果都是正确的, 如果其中有一个执行顺序出错, 则视为是出现bug, 即发生了线程安全问题
下面是一个有线程安全问题的代码:
package Thread;
class Add{
int a = 0;
public void add(){
for(int i = 0 ; i < 50000; i++){
a++;
}
}
}
public class ThreadDome11 {
public static void main(String[] args) {
Add a = new Add();
Thread t1 = new Thread(()->{
a.add();
});
Thread t2 = new Thread(()->{
a.add();
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("最终值为:" + a.a);
}
}
两个线程各自给a自增五万次, 最后的结果有可能小于, 等于五万, 也有可能大于五万, 等于十万等诸多结果
产生线程安全问题的原因
- 多线程之间抢占式执行, 随机调度(根本原因)
- 多个线程对同一变量进行修改操作
- 非原子操作(一行代码会被编译成多条机器指令), 只有非原子操作才会产生线程安全问题
- 内存可见性
- 指令重排序, 编译器在进行代码优化时, 可能会调整代码顺序
针对以上原因中, 非原子操作的解决方法最实用, 解决方法是将非原子操作原子化, 具体方法是通过上锁
通过上锁
可解决第二和第三个原因引起的线程安全问题
, 而第四和第五需要用volatile关键字
来解决, 这个下篇文章介绍, 本篇主要介绍上锁
上锁
java中上锁的关键字为synchronized
, 如果一个线程已经被加锁, 另一个线程也要加锁, 那么其就会陷入阻塞(CLOCKED)状态, 等待第一个线程被解锁之后再进行加锁操作, 此期间即使线程1不在cpu上执行, 线程2也不能被调度
将上诉代码进行如下修改:
class Add