并发编程之JMM&Volatile(一)

很多程序员应该对并发一词并不陌生,并发如同一把双刃剑,如果使用得当,可以帮助我们更好的压榨硬件的性能,反之,也会产生一些难以排查的问题。这里,先简单介绍下并发的几个基本概念。

进程与线程

进程:进程是操作系统进行资源分配和调度的基本单位。

线程:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

上面是百度百科对进程和线程的解释,可能有点抽象,这里笔者再根据自己的理解解释下进程和线程的概念和区别:当我们打开QQ、微信、网易云音乐,这时我们启动了三个进程,操作系统会分别对这三个进程分配资源,操作系统会分配什么资源给这三个进程呢?首先是内存资源,这三个进程都有各自的内存进行数据的存取,QQ和微信分别有各自的内存资源来保存我们的用户数据、聊天数据。其次,当我们需要用QQ或者微信聊天时,操作系统只会把键盘资源分配给QQ或者微信其中一个进程,当我们输入文字,只会出现在QQ或者微信其中一个的聊天窗。下面我们再来说说线程,我们用网易云音乐,可以同时下载音乐和播放音乐,两者互不影响,这是因为在网易云音乐这个进程里,同时有两个线程,一个线程播放音乐,一个线程下载音乐,利用多线程,可以使一个进程在一段时间内同时执行两个任务。

并发与并行

并发:在单核单CPU架构中,只会出现并发,不会出现并行。比如在一个电商系统中,用户A正在下单,用户B正在改名,因此分别有线程A和线程B两个线程在CPU上交替执行,互相竞争CPU资源。假设下单操作需要执行100个指令,改名操作需要执行60个指令,单核单CPU的架构可能先在线程A中执行80个指令,然后将CPU时间片让给线程B,线程B在执行50个指令后,CPU重新把时间片让给线程A执行剩余的20个指令,再执行线程B剩余的10个指令,最后线程A和线程B都执行完毕。

并行:只要是多核CPU,不管是单CPU还是多CPU,都有可能出现并行。还是以上面的电商系统为例,用户A和用户B的线程可以同时跑在同CPU或者不同CPU的不同的内核上,这时候就能做到线程A和线程B同时执行,互不竞争CPU内核资源。

区别:从上面的例子,我们可以知道并发和并行的区别,并发是指在一段时间内,多个任务交替执行,并行是同一时间内,多个任务可以同时执行。

并发编程的本质

至此,我们已经了解了并发的几个基本概念。而并发的本质是要解决:可见性、原子性、有序性这三个问题。

可见性

当多个线程同时访问同一个变量,一个线程修改了这个变量的值,其他线程要能立刻看到修改的结果。

我们来看下面这段代码,首先我们声明了一个静态变量flag,默认为true,线程A只要检查到flag为true时,就循环下去,主线程启动线程A后休眠2000毫秒,再启动线程B修改flag的值为false。按理来说,在flag被线程B修改为false之后,线程A应该退出循环。然而,如果我们运行下面的代码,会发现程序并不会终止。程序之所以不会终止的原因,是因为线程A无法跳出循环,即便我们用线程B把flag改为false,但线程B修改的行为,对线程A是无感知的,即线程A并不知道此时flag已经被其他线程修改为false,线程A仍旧以为flag为true,所以无法跳出循环。

public class VisibilityTest { private static boolean flag = true;//静态变量 public static void main(String[] args) { new Thread(() -> { int i = 0; while (flag) {//如果静态变量为flag则循环下去 i++; } System.out.println("i=" + i); }, "Thread-A").start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> flag = false, "Thread-B").start(); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdzzf.html