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比较类似,看下面的 *** 作示意图即可
第一次发文,如果对你有帮助,希望给我一个小赞赞。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)