前置知识
在此之前让我们了解一下操作系统,单处理器,多处理器,进程,线程的概念及它们之间的联系。
在早期的计算机中不包含操作系统,它们从头到尾只执行一个程序,并且这个程序能访问计算机中的所有资源。操作系统的出现使得计算机每次能运行多个程序,并且不同的程序都在单独的进程中运行:操作系统为各个独立执行的进程分配各种资源,包括内存,文件句柄以及安全证书等。如果需要的话,在不同的进程之间可以通过一些粗粒度的通信机制来交换数据,包括:套接字、信号处理器、共享内存、信号量以及文件等。
//未完待续todo add a picture
为了实现更加细粒度的控制,人们发明了线程。线程会共享进程范围内的资源,例如内存句柄和文件句柄,但每个线程都有各自的程序计数器、栈以及局部变量等。
线程也被称为轻量级进程。在大多数现代操作系统中,都是以线程为基本的调度单位,而不是进程。由于同一个进程中的所有线程都将共享进程内的内存地址空间,因此这些线程都能访问相同的变量并在同一个堆上分配对象,这就需要实现一种比在进程间共享数据粒度更细的数据共享机制。如果没有明确的同步机制来协同对共享数据的访问,那么当一个线程正在使用某个变量时,另一个线程可能同时访问这个变量,这就造成不可预料的结果。
//未完待续todo add a picture
多线程的好处
在双处理器系统上,单线程只能使用一半的CPU资源,而在拥有100个处理器的系统上,将有99%的资源无法使用。而多线程程序可以提高处理器资源的利用率。
然而不能说多线程在单处理器系统上毫无作用,它能帮助单处理器系统获得更高的吞吐率。如果一个线程在等待I/O操作完成,另一个线程可以继续运行,使程序能够在I/O阻塞期间继续进行。
线程的安全性问题
线程安全性是非常复杂的,在没有充分同步的情况下,多个线程中的操作执行顺序是不可预测的。
public class UnsafeSequence {
private int value;
//对value实行加一操作
public int getNext() {
return value++;
}
}
虽然递增运算value++看上去是个单个操作,但事实上它包含三个独立的操作:读取value,将value加1,并将计算结果写入value。由于运行时可能将多个线程之间的操作交替执行,因此这两个线程可能同时执行读操作,结果就是,在不同线程的调用中返回了相同的数值。
通过将getNext()修改为一个同步方法,可以修复UnsafeSequence中的错误。
public class Sequence {
private int Value;
public synchroized int getNext() {
return Value++;
}
}
如果没有同步,那么无论编译器、硬件还是运行时,都可以随意安排操作的执行时间和顺序,例如对寄存器或者处理器中的变量进行缓存,而这些被缓存的变量对于其他线程来说是暂时不可见的。