为什么我们需要考虑并发?不考虑的话会出现什么问题?
并发的多个程序(进程/线程)会对计算机资源进行争夺,如果不加以控制会出现混乱、严重影响程序运行效率,甚至错误
首先是对CPU时间片的争夺
对于多线程编程而言,由于创建线程后,线程的执行顺序是由调度程序控制的,也就是说各个线程的执行顺序并没有一个确定的预期,显然在很多情况下这会影响到我们的编程逻辑,所以我们首先需要一些方法能够实现对线程执行顺序时机的控制
其次,是对共享内存访问(读写)的问题
例如我们使用两个线程访问同一个共享的全局变量对其做大量的自加操作,由于:
- 首先自加操作并不是原子的
- 于是,例如
thread1在读取count的值比如0后,+1,但是还没写回内存之前,thread2读取了count的值,由于thread1的更改还没有写回内存,于是thread2读到的还是0
那么,最终的结果就是,原本预期是被加了两次结果是2的count值,最终得到的却是1
也就是说如果不对并发的内存访问加以控制,那么最终得到的count值和有可能是小于我们的预期的
这其实也是我遇到的一个面试题,面试官希望我举一个例子说明什么时候会出现并发问题
下面是一段简单的示例代码,用来说明上面的两种问题
public class CreateThread {
static int count = 0;// 一个全局共享变量
/**
* 一个线程内部类
*/
static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10000; i++) count++;
}
}
public static void main(String[] args) throws InterruptedException {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
thread1.join(); // 等待 thread1 执行完成
thread2.join(); // 等待 thread2 执行完成
System.out.println(count);
}
}
讲解一下
首先对于问题1,在程序中的体现在于:
我希望在两个子线程执行结束后,然后再打印最终的count变量,而不是子线程还在执行甚至还没执行就打印了count值,于是我使用了join()方法,它会使得主线程在子线程执行结束后再执行
对于问题2,在程序中的体现在于:
两个子线程在执行过程中分别访问并对count变量做了自加操作,各自10000次,我们预期结果应该是20000(当然我们知道这是不对的),但是结果得到了比如:17691、16976、12030