线程的六种状态

6个状态定义:java.lang.Thread.State

  1. New:尚未启动的线程的线程状态。
  2. Runnable:可运行线程的线程状态,等待CPU调度。
  3. Blocked:线程阻塞等待监视器锁定的线程状态。
    处于synchronized同步代码块或方法中被阻塞。
  4. Waiting:线程等待的线程状态。
    不带timeout参数的方式调用Object.wait、Thread.join、LockSupport.park
  5. Timed Waiting:具有指定等待时间的等待线程的线程状态。下列带超时的方式:
    Thread.sleep、Object.wait、Thread.join、LockSupport.parkNanos、LockSupport.parkUntil
  6. Terminated:终止线程的线程状态。线程正常完成执行或者出现异常
线程的六种状态.png

线程的终止

image.png

终止线程-interrupt

如果目标线程在调用Object class的wait()、wait(long)或wait(long, int)方法、join()、
join(long, int)、join(long, int)、sleep(long, int)或sleep(long, int)方法时被阻塞。
此时线程被调用interrupt方法后,该线程的中断状态将被清除,抛出InterruptedException
异常。
如果目标线程是被I/O 或者NIO中的Channel所阻塞,同样,I/O操作会被中断或者返回特殊异
常值。达到终止线程的目的。
如果以上条件都不满足,则会设置此线程的中断状态。
stop改成interrupt后,最终输出为“i=1 j=1”,数据一致。

正确的线程终止-标志位

代码逻辑中,增加一个判断,用来控制线程执行的中止。如右侧Demo4示例:


image.png

线程封闭

多线程访问共享可变数据时,涉及到线程间数据同步的问题。
并不是所有时候,都要用到共享数据,若数据都被封闭在各自的线程之中,就不需要同步,
这种通过将数据封闭在线程中而避免使用同步的技术称为线程封闭。

ThreadLocal 线程封闭

ThreadLocal是Java里一种特殊的变量。
它是一个线程级别变量,每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,
竞争条件被彻底消除了,在并发模式下是绝对安全的变量。
用法:ThreadLocal var = new ThreadLocal();
会自动在每一个线程上创建一个T的副本,副本之间彼此独立,互不影响。
可以用ThreadLocal存储一些参数,以便在线程中多个方法中使用,用来代替方法传参的做法。
实在难以理解的,可以理解为,JVM维护了一个Map,每个线程要用这个T的时候,用当前的线程去Map
里面取。仅作为一个概念理解

栈封闭

局部变量的固有属性之一就是封闭在线程中。
它们位于执行线程的栈中,其他线程无法访问这个栈。

CPU缓存以及内存屏障

为了提高程序运行的性能,现代CPU在很多方面对程序进行了优化。
例如:CPU高速缓存。尽可能地避免处理器访问主内存的时间开销,处理器大多会利用缓存
(cache)以提高性能


image.png

CPU高速缓存中的数据是内存中的一小部分,但这一小部分是短时间内CPU即将访问的,
当CPU调用大量数据时,就可避开内存直接从Cache中调用

多级缓存

  • L1 Cache是CPU第一层高速缓存,它的容量非常小,一般为32-256KB,提高容量所带来的技
    术难度增加和成本增加非常大。
  • L2 由于L1高速缓存的容量限制,为了再次提高CPU的运算速度,在CPU外部放置一高速存储
    器,即二级缓存,现在家庭用CPU容量最大的是4MB,而服务器和工作站上用CPU的L2高速缓存
    普遍大于4MB,有的高达8MB或者19MB。
  • L3 现在一般都是内置的。在拥有三级缓存的CPU中,只有约5%的数据需要从内存中调用,进
    一步提升了CPU效率,同时提升大数据量计算时处理器的性能。具有较大L3缓存的处理器提供
    更有效的文件系统缓存行为及较短消息和处理器队列长度。一般是多核共享一个L3缓存!
    CPU在读取数据时,先在L1中寻找,再从L2寻找,再从L3寻找,然后是内存,再后是外存储器。

缓存同步协议

  • 多CPU读取同样的数据进行缓存,进行不同运算之后,最终写入主内存以哪个CPU为准?
  • 在这种高速缓存回写的场景下,有一个缓存一致性协议(MESI协议)多数CPU厂商对它
    进行了实现。
  • 多处理器时,单个CPU对缓存中数据进行了改动,需要通知给其他CPU。也就是意味着,
    CPU处理要控制自己的读写操作,还要监听其他CPU发出的通知,从而保证最终一致。

CPU运行时性能优化手段 指令重排

image.png

指令重排的场景:当CPU写缓存时发现缓存区块正被其他CPU占用,为了提高CPU处理性能,可能将后面的读缓存命令优先执行as-if-serial语义:指令重排时,不管怎么重排序,单个线程的执行结果不能被改变。换句话说,编译器和处理器不会对存在数据依赖关系的操作做重排序。

指令重排带来了两个问题

  1. CPU高速缓存下有一个问题:缓存中的数据与主内存的数据并不是实时同步的,各CPU(或CPU核心)间缓存的数据也不是实时同步。在同一个时间点,各CPU所看到同一内存地址的数据的值可能是不一致的。
  2. CPU执行指令重排序优化下有一个问题:虽然遵守了as-if-serial语义,单仅在单CPU自己执行的情况下能保证结果正确。多核多线程中,指令逻辑无法分辨因果关联,可能出现乱序执行,导致程序运行结果错误。

内存屏障

处理器提供了两个内存屏障指令(Memory Barrier)用于解决上述两个问题:
写内存屏障(Store Memory Barrier):在写指令后插入Store Barrier,能让写入缓存中的
最新数据更新写入主内存,让其他线程可见。
强制写入主内存,这种显示调用,CPU就不会因为性能考虑而去对指令重排。
读内存屏障(Load Memory Barrier):在读指令前插入Load Barrier,可以让高速缓存中的
数据失效,强制从新从主内存加载数据。
强制读取主内存内容,让CPU缓存与主内存保持一致,避免了缓存导致的一致性问题

你可能感兴趣的:(线程的六种状态)