Java并发编程02 -- Java 中的线程技术

本文章着重去讲解Java中如何使用多线程技术,并且介绍一下线程常见的技术

Java中创建线程的四种方式

1. 实现Thread类 重写Run方法

在这里插入图片描述
如图所示,去创建ThreadTest类后 直接调用start方法,即可额外启动一个线程,这里有一个小小的注意点,不能去调run方法,如果直接调用run方法,就和普通方法一样,在主线程下执行。
一般不推荐这样使用,因为Java单继承的特性,如果使用了这个方法,就无法继承其他的类了。

2. 使用 Runnable 配合 Thread类去实现多线程

在这里插入图片描述
线程类实现Runnable接口,并且作为一个参数传入Thread类,就可以得到一个Thread类对象,调用start方法即可开启线程执行。
该方法比继承Thread类的好处在不会占用一个继承的名额,如果类已经有需要继承的类了,依然可以使用runnable方式实现多线程。

3. 实现Callable接口 使用FutureTask 来实现多线程
3.1 Callable接口介绍

在这里插入图片描述
如上图可知 Callable接口类似Runnable接口,但是这个接口可以返回一个结果,并且可以抛出异常,通常用于处理一个线程 需要另一个线程执行结果的情况来使用。

3.2 使用步骤
  1. 创建一个实现了Callable接口的类 并且实现call方法
  2. 创建一个FutureTask对象,将上面实现了Callable接口的类传入FutureTask中
  3. 创建线程对象 将FutureTask对象传入线程对象即可
    在这里插入图片描述
4. 线程池 (本文不详细介绍,后续文章会详细介绍线程池)
关于实现Runnable接口和实现Thread类的源码探究

在这里插入图片描述
这个是Thread类中的run方法 其中target是一个runnable类型的对象 如果target不为空 则去调用runnable接口的run方法,否则什么都不做,所以为什么实现Runnable接口可以实现创建多线程也就很明白了,因为我在Thread类的构造中传入了这个target,接下来看一下源码去验证。

在这里插入图片描述

 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
		// 这里省略了很多代码,就是为了给大家看一下 传入runnable就是在初始化线程对象将target赋值了
        this.target = target;
        
        /* Set thread ID */
        tid = nextThreadID();
    }

验证过了这里之后,我们要去考虑一下为什么我继承Thread类,实现Run方法,也没有去给target赋值,为什么也可以实现多线程呢,看下面的一张图就明白了。
在这里插入图片描述
因为Thread类,本身就实现了Runnable接口,即Thread类本身就是一个target对象。 所以重写Thread类的run方法,其实实现的还是runnable接口的run方法,看到这里应该就没有什么疑惑了。

常用的查看进程的方式

在这里就介绍几个常用的命令,更多命令以及工具,在JVM篇调优中写(挖坑小能手)

jps 查看Java进程
jstack 查看进程号为PID的线程状态
jconsole 查看Java资源监控
在这里插入图片描述
注: jconsole工具在线程中比较重要,可以跟踪死锁等,会在后面介绍。

Java 线程常用API

Java线程中AP比较多,在这里之侧重介绍几个常用的。剩下的会贴一个API文档供大家参考

start() : 启动一个新线程,在新的线程运行run方法中的代码
注意点:start方法并不是让线程立即启动,只是告诉CPU,我这个线程可以运行了,需要等待分配时间片运行
run(): 线程启动后会执行的方法
join():等待线程运行结束 (可以加Long类型参数 代表最多等待多少毫秒)
get/setName():获取设置线程名字
get/setPriority:获取设置线程优先级
注意点:优先级设置高只是会增大被执行的概率,具体还是要看CPU分配调度
interrupt():打断线程 并且设置打断标记 如果被打断的线程本身就处于非运行状态 则会抛出线程被打断异常
interrupted():判断当前线程是否被打断,会清除打断标记
isInterrupted() :判断当前线程是否被打断 不会清除打断标记
sleep(Long time): 使当前线程休眠多少毫秒
yield(): 线程礼让

简述线程切换的原理

在这里只介绍栈帧和上下文切换,关于栈帧详细的介绍会在本人JVM学习笔记中去介绍详细的。

线程运行主要占用Java Virtual Machine Stacks (Java 虚拟机栈),每一个线程都会在栈中产生一个叫做栈帧(Stack Frame)的单位,其中保存着这个线程运行的一些信息。

上下文切换( Thread Context Switch):线程之间 从一个线程执行切换到另一个线程执行的过程
发生上下文切换的原因

  1. 被动:
    线程的 cpu 时间片用完
    出现垃圾回收,导致STW(Stop The Word) 所有用户线程停止
    有更高优先级的线程需要运行
  2. 主动
    线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

注意事项:
当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的
状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等

Context Switch 频繁发生会影响性能

线程的状态

在操作系统角度上,线程共分为五种状态
在这里插入图片描述

其中创建的线程为初始状态,执行中不断地在可运行,运行以及阻塞状态间切换,生命周期结束后变为终止态。

在Java中,线程共分为六种状态(Thread.State)在这里插入图片描述
NEW:线程刚被创建,但是还没有调用 start() 方法
RUNNABLE:当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的
【可运行状态】、【运行状态】和【阻塞状态】
(由于 BIO blocking I/O 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行)
BLOCKED , WAITING , TIMED_WAITING 分别是等锁,无期限等待,有期限等待状态。

TERMINATED 当线程代码运行结束


版权声明:线程状态部分 图片来源黑马程序员,在此承诺本文不用作盈利,不存在任何商业行为,仅作为个人笔记。

全面发展,一专多能,今天也是一个想进大厂的菜鸡哈!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值