我们给出结果:
19:22:27 [t3] c.ThreadStarter - hello19:22:27 [main] c.ThreadStarter - 结果是:100
我们给出简单解释:
- FutureTask内置了一个Callable对象,初始化方法将指定的Callable赋给这个对象 。
- FutureTask实现了Runnable接口 , 并重写了Run方法,在Run方法中调用了Callable中的call方法,并将返回值赋值给outcome变量
- get方法就是取出outcome的值 。
// 下述的操作不受我们控制,谁先调用,调用多久都不是我们管控的@Slf4j(topic = "c.TestMultiThread")public class TestMultiThread {public static void main(String[] args) {new Thread(() -> {while(true) {log.debug("running");}},"t1").start();new Thread(() -> {while(true) {log.debug("running");}},"t2").start();}}
我们直接给出结果展示:23:45:26.254 c.TestMultiThread [t2] - running23:45:26.254 c.TestMultiThread [t2] - running23:45:26.254 c.TestMultiThread [t2] - running23:45:26.254 c.TestMultiThread [t2] - running23:45:26.254 c.TestMultiThread [t1] - running23:45:26.254 c.TestMultiThread [t1] - running23:45:26.254 c.TestMultiThread [t1] - running23:45:26.254 c.TestMultiThread [t1] - running23:45:26.254 c.TestMultiThread [t1] - running23:45:26.254 c.TestMultiThread [t1] - running
查看进程线程方法由于不同系统的查看方法不同,我们主要介绍三种类型查看方法Window
- 任务管理器可以查看进程和线程数 , 也可以用来杀死进程
- tasklist 查看进程 :tasklist| findstr (查找关键字)
- taskkill 杀死进程:taskkill /F(彻底杀死)/PID(进程PID)
- ps -fe 查看所有进程
- ps -fT -p查看某个进程(PID)的所有线程
- kill 杀死进程 top 按大写 H 切换是否显示线程
- top -H -p查看某个进程(PID)的所有线程
- jps 命令查看所有 Java 进程
- jstack查看某个 Java 进程(PID)的所有线程状态
- jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)
栈与栈帧下面我们来介绍一下与进程息息相关的底层原理:
- 栈:存放栈帧的个体 , 每个线程具有一个单独的栈来存放多个栈帧
- 栈?。悍椒ǖ娜磕诖嬲加? ,每个方法使用时的全部内存都用一个栈帧来表示
- 每个栈由多个栈?。‵rame)组成,对应着每次方法调用时所占用的内存
- 每个线程只能有一个活动栈?。杂ψ诺鼻罢谥葱械哪歉龇椒?/li>
// 这里展现的是单线程,目前只有一个栈!package cn.itcast.n3;public class TestFrames {// 首先main方法被调用,所以main方法先进入栈中 , 在main方法执行结束后被抛出栈public static void main(String[] args) {method1(10);}// 由于main方法调用了method1,所以栈中在存有main栈帧的同时也将method1栈帧调入,在method1方法执行完毕后抛出private static void method1(int x) {int y = x + 1;Object m = method2();System.out.println(m);}// 由于method1方法调用了method2,所以栈中在存有main,method1栈帧的同时也将method2栈帧调入,method2方法执行完毕后抛出private static Object method2() {Object n = new Object();return n;}}// 这里展现的是多线程,主线程和线程t1独自各占有一个栈,互不影响!package cn.itcast.n3;public class TestFrames {// 这里会产生两个栈,两个栈互不影响,两个栈都会顺序调用main,method1,method2栈?。承虿欢?public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run() {method1(20);}};t1.setName("t1");t1.start();method1(10);}private static void method1(int x) {int y = x + 1;Object m = method2();System.out.println(m);}private static Object method2() {Object n = new Object();return n;}}
线程上下文切换我们再来介绍一下上下文切换:- 当出现一些状况时,系统会自动将CPU的使用权进行切换,交付给不同的线程进行使用
- 要由操作系统保存当前线程的状态,并恢复另一个线程的状态 ,
- Java 中对应的就是程序计数器,它的作用是记住下一条 jvm 指令的执行地址,是线程私有的
- 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
- 线程的 cpu 时间片用完
- 垃圾回收
- 有更高优先级的线程需要运行
- 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
推荐阅读
- Seata 1.5.2 源码学习
- 第4版 高性能MySQL 第一章 MySQL架构 读书笔记
- Helm干货!速度围观!
- .NET 源码学习 [数据结构-线性表1.2] 链表与 LinkedList<T>
- MPC:百万富翁问题
- PGL Paddle Graph Learning 关于图计算&图学习的基础知识概览:前置知识点学习
- JVM学习笔记——内存模型篇
- Linux学习环境搭建流程
- Archlinux + Dwm 配置流程
- 关于入门深度学习mnist数据集前向计算的记录