目录:
java线程内存模型
-
主内存和工作内存
Java的线程内存包括主内存和工作内存。
这里的内存模型和jvm的内存区域并没有直接的关系,如果非要对应起来,那么主内存对应的是java堆,工作内存对应的是虚拟机栈。
工作内存是每个线程私有的,而主内存则是共享的。
线程并不直接操作主内存的数据,而是先操作工作内存里保存的副本,然后再同步到主内存中。
-
volatile的特殊规则
volatile有两个作用- 可见性:所谓可见性就是当一个线程修改了变量后,其他的线程都可以立刻观察到。那普通的变量就不能立刻观察到吗?是的,因为线程都是直接操作工作内存,如果只是读取,那么只是读取工作内存中的副本,并不会察觉到主内存发生了变化。
而volatile的作用就是值发生变化时,其他线程的工作内存中的副本全部失效,必须重新从主内存获取。这样就能够获取到最新的数值了,也就实现了其他线程可以立刻观察到数据发生了变化。不过这种实现也导致了对数据进行修改时,效率要更低,因为必须通知其他线程,该变量已经被修改。 - 禁止指令重复排序
我们以为代码会按照我们编写的顺序去运行,但是实际上并不是这样的。为了优化,虚拟机会把一些逻辑无关的代码重新排序,以加快运行速度,这在单线程的环境下是没有问题的,但是在多线程的环境下却可能发生问题,而被标记了volatile的变量,相关的代码就不会被重排序了。
- 可见性:所谓可见性就是当一个线程修改了变量后,其他的线程都可以立刻观察到。那普通的变量就不能立刻观察到吗?是的,因为线程都是直接操作工作内存,如果只是读取,那么只是读取工作内存中的副本,并不会察觉到主内存发生了变化。
boolean initialized=false;
public pre(){
readConfig();//这里是一个读取配置的方法
initialized = true;//当读取配置完成时,设置为true
}
public foo(){
while(!initialized){
//当未初始化完成时,休眠
Thread.sleep();
}
//做其他事情
}
上面的代码中,pre()和foo()分别在两个线程中运行,foo只有等待初始化后才会继续执行,但是pre中的代码,从单线程的角度看,以下的两句代码并没有逻辑关系。
readConfig();
initialized = true;
所以有可能会将其顺序倒置未以下代码,那么就会出现问题,foo()函数的判断就是错误的。
//修改顺序后代码
initialized = true;
readConfig();
所以应该用 volatile 修饰initialized,这样子他的顺序就不会重排了。
volatile boolean initialized=false;
- 先行发生原则
先行发生原则就是有一些代码必须按照顺序执行。
1.传递性
如果代码之间是由关系的,那么他们的顺序是不能调整的,必须按照代码顺序执行,如下面的代码。
int a = 0;
int b = a;
int c = b;
2.使用volitile关键子修饰
3.程序次序,如if语句等,必须先执行if判断
4.线程的启动,终止,中断,必须按照顺序。没有启动前当然不能终止了。
5.对象终结:只有对象初始化完成才可以终结。
线程的实现
-
线程的实现
线程的实现有两种方案,其一是一对一映射到内核线程,使用内核提供的轻量级线程。这样做的好处是线程的切换,多处理器的同步等复杂问题都由内核去实现。缺点是操作线程必须调用内核进行操作,需要在用户态和内核态之间进行切换。其次,每个线程都对应一个内核线程,需要耗费更多的资源。
其二是直接使用用户线程来实现多线程,即在一个线程中模拟多个线程。优点是不需要和内核进行交互,效率高,缺点是复杂度高,必须自己实现诸如“阻塞如何处理”,“多处理器映射”等问题。
java1.2之后的版本,使用的是第一种方案。 -
线程调度
我们都知道CPU是以时间片的方式来运行线程的,即每一个线程运行一个时间片后,便让出CPU资源给另外一个线程使用,达到一个貌似同时运行的效果,这种线程调度方式称之为抢占式调度。由于java的线程实际上是由内核控制的,所以java的线程调度也是抢占式调度。
虽然java代码不能对线程的调度进行影响,但是可以影响其被调度的概率。即设置线程的优先级。前调下优先级并不能保证线程的运行顺序,只能保证优先级高的线程会有更大的几率被运行。而且由于不同平台内核优先级并不一致,所以使用优先级并不靠谱。 -
状态转换
- new:新建线程
- runable:包含ready,runing。由于线程会一直在ready和runing直接切换,所以并不区分
- watting:使用object.wait(),Thread.join(),和LockSuport.park()方法等
- Time Waiting,使用Thread.sleep()和Object.wait(设置时间)
- Blocked:获取锁失败后
- Terminated:线程终止