一、进程和线程
1、进程
a、拥有自包含的执行环境
b、拥有完整的、私有的运行时资源的集合
c、拥有自己的内存空间
进程的同义词有“程序”、“应用”,pipe、socket能够为一个系统中的不同进程,或者不同系统中的多个进程。
2、线程
a、轻量级的进程,存在于进程之中
b、线程与进程共享内存、打开的文件
对于开发者而言,启动的线程只有一个,那就是main()。
Thread.slepp方法保证休眠的时间,因为时间会受到底层操作系统的影响。同时,休眠期间有可能会被Interrupt。
即使代码中没有使用可能抛出异常的方法,也可以显式的判断有没有异常(比如用if),有的话进行处理。
isInterrupt方法在alive的线程未被Interrupt时,返回false;不是alive的线程被Interrupt时也返回false。
join()方法允许一个线程等待另一个线程执行结束再继续执行。
3、线程冲突
多个线程同时引用同一个对象,并且操作不是原子操作的时候,有可能会出现线程冲突。
比如;i++操作,在jvm层面,该操作分为三步,多个线程同时执行i++,会出现interleave的情况,不能够保证每次执行(对应的jvm层面分解之后的操作)的顺序都是一样的。
4、内存不一致错误
对于多个线程共享的对象,一个线程对该对象写内存之后,对其他线程是可见的。
例如:Thread A的修改对Thread B 不可见(A、B访问同一个对象)
原始counter = 0;
A线程先执行了counter++ ;但是B线程却打印counter=0(正确情况应该是1),那可能是因为A对counter的修改于B时不可见的,A、B之间并没有建立起happens-before的关系。
解决方法:线程之间建立happens-before关系(比如使用join())。
5、同步方法
a、需要有一个共同的对象
b、不能用在构造方法上,因为线程需要先创建对象然后再使用它。即便如此,也需要避免对象过早泄露。例如在构造函数中使用instances.add(this),在构造对象完成之前,其他的线程已经能够通过instances访问该对象。
final类型的变量,对非同步方法而言是读安全的,但是有可能会引起liveness的问题。
c、实现机制是使用了内部锁(监视锁),来达到以下目的
1、强制对引用对象的独占访问
2、建立起happens-before关系
d、如果同步方法是static的,锁为Class对象
6、同步方法块
a、需要指定一个对象充当内部锁
b、支持重入同步(同步方法块中的代码又调用了其他同步代码块中的代码,而且都使用相同的锁,循允许线程多次获得同一把锁来避免自身将自身阻塞)。
7、原子操作
1、操作要么完整执行,要么根本不执行,不存在中途停止执行的情况。
2、c++不是原子操作
3、原子操作不存在线程干扰问题,但是有可能出现内存不一致问题。
4、所有的引用变量以及基本数据类型变量(除了long、double)的读写操作都是原子操作。
5、所有被volatile修饰的变量(包括long、double)的读写操作都是原子操作。