Java 线程概述: 线程种类、状态,原子性、内存可见性、synchronized、volatile

来源:互联网 发布:阿里云做的怎么样 编辑:程序博客网 时间:2024/04/29 07:17

创建一个线程实例时,JVM会分配两个调用栈,一个用于跟踪java代码间的调用关系,一个用于跟踪native代码间的调用关系


线程种类

守护线程(Daemon Thread)和用户线程(User Thread)。

用户线程会阻止JVM的正常停止,即JVM正常停止前进程中的所有用户线程必须先停止完毕,否则JVM无法停止

守护线程则不会影响JVM的正常停止

设置线程为守护线程,可以调用Thread的setDaemon(true); 


线程的状态

 线程状态定义在 enum Thread.State

NEW:刚创建而未启动的线程状态,该状态只有一次

RUNNABLE:线程运行中的状态

BLOCKED:阻塞式状态。如一个线程发起了阻塞式I/O操作(如文件读写和阻塞式Socket读写),或试图去获得一个

其他线程的锁时,会处于该状态。当解除阻塞状态,会转为RUNNABLE状态

WATING:无限其他线程执行的状态。在执行了object.wait()、thread.join()、和LuckSupport.park()后,就会处于该状态

      要想从WATING转为RUNNABLE的相应方法有object.notify()、object.notifyAll()、LockSupport.unpark(thread)。

TIMED_WAITING:与WAITING类似,不过不是无限等待,而是有时间限制的。如方法Thread.sleep(long millis)、

      object.wait(long millis)、thread.join(long millis)、LockSupport.parkNanos(long nanos)、LockSupport.parkUtil(long deadline);

TERMINATED:已经执行结束的线程状态,该状态只有一次


Context切换

多线程环境中,当一个线程的状态由RUNNABLE转换为非RUNNABLE(BLOCKED、WATING、TIMED_WAITING)状态时,

相应线程的上下文Context信息需要被保存,以便相应的线程稍后再次进入RUNNABLE状态时,恢复之前的Context信息。

Context的切换会带来额外的开销,这包括保存和恢复线程的Context信息的开销、对线程进行调度的CPU时间开销及CPU

缓存内容失效(失效后,会重新访问主内存中的变量以重新创建CPU缓存内容)的开销


原子性

原子(Atomic)操作指相应的操作是单一不可分割的操作。如i++操作,在多线程中,可能a线程在赋值时,已经被b线程修改了值,

而a线程拿到的过期值,这就跟我们的期望不符了。使用synchronized可以实现原子性


synchronized关键字

其所包括的区域内,具有排他性,即任一时刻只能有一个线程进入该代码块,这就使得代码块的操作是一个原子操作


内存可见性

CPU在执行代码的时候,为了减少变量的访问时间的消耗,可能将代码中的访问变量值缓存到该CPU的缓存区,

再次访问该变量时,可能从缓存区读取,而不是主内存;同样地,代码对缓存变量进行了修改,也可能没有写回主内存。

由于每个CPU都有自己的缓存区,因此一个CPU缓存区中的内容对于其他CPU来说是不可见的。

synchronized保证了,一个线程对代码块内变量值的修改,对于稍后执行的其他执行该代码块的线程来说是可见的


volatile

只能保证修饰变量的内存可见性。当一个线程修改了以volatile修饰的变量值时,它会写入主内存,
并强制其他使用该变量的线程都刷新成主内存的最新值。
synchronized相比,前者既保证操作的原子性,又能保证内存可见性。
而volatile仅能保证内存可见性,但是,前者会导致Context切换,而后者不会
因volatile不保证原子性,所以在多线程环境中,仅使用volatile是不够的,需要加入锁的机制 




0 0
原创粉丝点击