线程深入理解

JVM运行时数据区

  • 线程共享部分 线程独占部分
    方法区 本地方法栈
    堆内存 虚拟机栈
    程序计数器
  • 线程共享部分:所有线程都能访问这块数据区域,随虚拟机的创建,随GC销毁。

  • 线程独占部分:每个线程的独立空间,随线程创建和销毁

线程实现/创建的方式

  • 继承Thread

  • 1
    2
    3
    4
    5
    6
    7
    8
    public class MyThread extends Thread{
    @override
    public void run(){
    sout("实现线程");
    }
    }
    MyThread myThread=new MyThread();
    myThread.start();
  • 实现runnable接口

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    public class MyThread implements runnable{
    @override
    public void run(){
    sout("实现线程");
    }
    }
    MyThread myThread=new MyThread();
    Thread thread=new Thread(myThread);
    thread.start();

线程状态

  • new:新建,尚未启动的线程的线程状态
  • runnable:可运行状态,等待cpu的调度
  • blocked:阻塞。
  • waiting:等待线程的线程状态
  • timed waiting:具有指定等待时间的线程状态
  • Terminated:线程终止状态,线程正常完成或出现异常

线程状态切换图

线程的中止

  • stop:错误的中止,可能破坏线程安全
  • interrupt:正确的中止,不会破坏线程安全

sleep()和wait()的区别

  • sleep()属于Thread类。wait()属于Object类
  • sleep不会释放对象锁,指定时间到了会自动恢复运行。
  • wait()会释放锁,需要notify唤醒才会进入运行状态

start和run的区别

  • start()来启动线程,无需等待run()方法体执行完毕,可以直接继续执行下面的代码
  • start()方法仅仅是启动一个线程,使线程处于就绪状态。
  • run()方法称为线程体,执行run()方法,线程就进入了运行状态。

线程的调度

suspend/resume wait/notify park/unpark
已弃用 会造成永久等待,同理与suspend和resume 造成死锁,park不会释放锁
容易造成死锁和永久等待
死锁:synchronized同步代码块不会释放锁,导致不能resume唤醒线程
永久等待:在挂起之前,进行了sleep操作,还未及时挂起,线程便已经唤醒了,顺序不对,导致永久等待

线程的封闭

  • 多线程访问共享可变数据时,涉及到线程间数据同步问题,并不是所有时候,都要用到共享数据,所以线程封闭概念就提出了
  • 数据都被封闭在各自的线程中,就不需要同步,这种通过将数据封闭在各自线程中而避免同步的技术称为线程封闭。
  • 线程封闭具体体现有:ThreadLocal,局部变量

线程池

线程池原理-概念

  • 线程池管理器:用于创建并管理线程。包括创建线程,销毁线程池添加新任务
  • 工作线程:线程池中的线程,在没有任务时处于等待状态,可以循环的执行任务
  • 任务接口:任务必须实现的接口,以供工作线程调度任务的执行,主要规定了任务的入口,任务执行完成后的收尾工作,任务的执行状态等。
  • 任务队列:用于存放没有处理的任务,提供一种缓冲机制。

任务Execute的执行过程

if(核心线程数量<设定的核心最大线程数量){

​ 新建线程

}else{

​ if(任务队列数量<设定的任务队列最大值){

​ 丢到任务队列

​ }else{

​ if(最大线程数<设定的最大线程数量){

​ 新建线程

​ }else{

​ 拒绝执行

​ }

​ }

}

volatile关键字

  • 线程可见性:保证了被volatile修饰的变量是共享的,该变量的修改,都能及时被其他线程看到。
    • 原因
      • 不做指令重排序
      • 不做缓存

线程安全之原子操作

  • 多个线程访问了相同的资源,向这些资源做了写操作,对执行顺序有着严格的要求。
  • 临界区:incr的内部就是临界区,多线程的并发执行可能对该结果产生影响。
  • 竞态条件:可能发生在临界区的特殊条件,多线程执行inrc内部的i++;产生了竞态条件。

判定线程安全

  • 没有共享资源。
  • 没有提供setter。update相关数据的方法。

原子操作的定义

  • 原子操作可以是一个步骤,也可以是一系列的操作,但是顺序不能打乱,也不能被切割只进行一步,必须所有执行完毕。(不可中断性)。例如i++.步骤为读取i,i+1.赋值给i.顺序缺一不可,也不可打乱。
  • 将整个操作视为一个整体是原子性的核心特征。

CAS的定义

  • CAS:compare and swap,处理器提供了基本内存操作的原子性保证。CAS操作每次操作时,都需要输入两个值,一个旧值一个新值。每次操作前都要比较旧值有没有发生变化,如果没有发生变化则交换新值,否则保持不变。
  • Atomic工具类
  • jdk1.8新的API加强方法:LongAdder

CAS的三个问题

  • 循环+cas,自旋的实现让所有线程都处于高频运行,争抢cpu的执行时间的状态。如果操作长时间不成功会带来很大的cpu损耗。
  • CAS只能适用单个变量i的操作,不适用于多个变量j的原子操作。
  • ABA问题(代码理解)
    • 线程-1:将A->B
    • 线程-2将A->C
    • 线程-3将B->A
      • 本来线程2操作会失败,但由于线程-3将B->A.线程-2又成功了
-------------The End-------------
0%