cpu调度每一个线程,分配线程时间片的时候,该线程才会被执行,由于分时操作,当发生指令交错的时候,就会引起线程安全问题。
一个程序运行多个线程是没有问题的,问题出现与多个线程访问共享资源,对共享资源读写操作时,就容易出现问题。举一个例子,下面的代码中,main方法中定义了两个线程,线程t1对counter变量进行5000加操作,t2线程对counter变量进行了5000次减操作。启动两个线程,等到两个线程运行结束之后,counter变量会是多少呢?答案是正数、负数、0都有可能。因为两个线程都对counter变量进行操作,当发生指令交错,最终的结果是未知的。
static int counter = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
counter++;
}
}, "t1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
counter--;
}
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter);
}
看下面的情况,两个线程对主内存中的i变量分别进行自增和自减,刚才说过会出现三种情况,我们来看一下三种情况对应的底层指令是如何进行的。
- 情况1:结果为0
这种情况非常简单,线程1将i取出,对i自增,再将i放入到内存中,操作完之后,线程2将i取出,i自减,再将i放回,只要每个线程将i取出,并对i放回之间,不被其他线程打扰,换句话说,一个县城对i操作的过程中,不被其他线程打扰,i最终的值都是0。
重点的结果不为0的情况,也就是一个线程对i操作时,被其他线程打断了(指令交错)。
- 情况2:结果为正数
刚开始i为0,线程2将i取出,进行自减,i变为-1,还没有将i放入到主内存中,cpu说,你的时间片用完了,保存你现在的状态,准备走吧,线程2将i = -1记下,并将程序计数器指向下一行将要操作的指令(将i放入到主内存中)。
线程1获得时间片,开始执行,由于线程2还没有将i的值更新,因此此时i依然为0,将i从主内存中取出,i自增变为1,再将i = 1更新到主内中,此时i 变为1。
线程2再次获得了时间片,拿到时间片之后,线程2回复原来的状态(i = -1),然后开始执行程序计数器指向的指令,将i = -1 更新到主内存中,最终i = -1。
- 情况3:结果为正数
和情况2比较类似,看下面的操作示意图即可
第一次发文,如果对你有帮助,希望给我一个小赞赞。



