# 1.创建线程和运行线程

  • 1.1直接使用 Thread
// 构造方法的参数是给线程指定名字,推荐
Thread t=new Thread("t1"){
    @Override
    public void run() {
        //要执行的任务
    }
};
//启动线程
t.start();
  • 1.2使用 Runnable 配合 Thread
// 采用Java8  lambda表达式的方式
Runnable runnable=()->{
    //要执行的任务
};
Thread thread=new Thread(runnable,"t2");
thread.start();

1.1和1.2方法分析 通过源码很容易可以看出使用Runnable的方式其实就是将runnable的方式写进了Thread类的run方法中

image.png 如果target对象不等于空的话就调用target.run()方法。

而方法一其实就是重写了Thread类的run方法。其实本质区别不大。

  • 1.3 FutureTask 配合 Thread FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
FutureTask<Integer> futureTask=new FutureTask<>(()->{
    return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(futureTask,"t3").start();
//get方法返回结果
Integer result = futureTask.get();
System.out.println(result);

# 2.线程运行原理

  • 2.1 栈与栈帧: 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存。每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。 大白话就是一个程序执行之后会在一个栈运行,每执行一个方法就会创建一个栈帧(也就是为什么 局部变量、方法参数和返回值地址会存储在栈帧中,因为方法里面也就包括这几样东西),当方法执行完了栈帧就会被回收,同时也提供返回值的地址。如果是多线程的话,可以这样理解线程就是以栈帧为单位的,每个线程都有自己的栈内存并且里面有多个栈帧,线程的栈内存是相互独立的,之间互不干扰。

  • 2.2 线程上下文切换(Thread Context Switch):就是从使用cpu到不适用cpu 就是一次线程上下文切换 例如:

    • 线程的 cpu 时间片用完
    • 垃圾回收
    • 有更高优先级的线程需要运行
    • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

    当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的