多线程系列

线程的六种状态

2020-01-24  本文已影响0人  小蜗牛Aaron

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<T> var = new ThreadLocal<T>();
会自动在每一个线程上创建一个T的副本,副本之间彼此独立,互不影响。
可以用ThreadLocal存储一些参数,以便在线程中多个方法中使用,用来代替方法传参的做法。
实在难以理解的,可以理解为,JVM维护了一个Map<Thread, T>,每个线程要用这个T的时候,用当前的线程去Map
里面取。仅作为一个概念理解

栈封闭

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

CPU缓存以及内存屏障

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


image.png

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

多级缓存

缓存同步协议

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缓存与主内存保持一致,避免了缓存导致的一致性问题

上一篇 下一篇

猜你喜欢

热点阅读