阅读本文约“12分钟”
适读人群:Java初级
学习笔记
基础概念
线程是无处不在的
先说说几个基本的概念吧
一个进程中可以包含多个线程,同一个进程中的线程共享该进程所申请到的资源,如内存空间和文件句柄等
从JVM的角度来看,线程是进程中的一个组件(Component)
Java程序中任何一段代码总是执行在某个确定的线程中
Java中线程分为守护线程(Daemon Thread)和用户线程(User Thread)
用户线程:JVM正常停止前应用程序中的所有用户线程必须先停止完毕,否则JVM无法停止
守护线程:不会影响JVM的正常停止,通常执行一些重要性不高的任务,如监视其他线程的运行情况
在多线程的运行中,我们需要注意每个段代码是由哪一个线程去负责执行的,这关系到性能问题、线程安全
System.out.println("The ** method was executed by thread: " + Thread.currentThread().getName());
如上可以看看对应方法是哪个线程负责执行的,当然你可以创新一个新的线程,并由新的线程负责,来验证你的猜想
创建并运行
在Java中,一个线程就是一个java.lang.Thread的实例
创建一个Thread类,JVM会为这个线程实例分配两个调用栈(Call Stack)所需的内容空间
两个调用栈,一个用于跟踪Java代码间的调用关系,另一个用于跟踪Java代码对本地代码(即Native代码)的调用关系
假设我们在main方法中创建了一个新的线程,那么新的thread就是main线程的子线程,它们也就是父子线程关系
在默认情况下父线程为守护线程,则子线程也同样为守护,用户线程也是如此,当然你也可以通过setDaemon方法来修改这一属性
状态与上下文切换
人这一生有很多种状态,线程也是一样的
我们可以通过getState方法获取,返回值是Enum(枚举)
而一个线程从RUNNABLE转换为BLOCKED、WAITING、TIMED_WAITING几个状态的过程就意味着上下文切换(Context Switch)
上下文信息:包括CPU的寄存器和程序计数器在某一时间点的内容等
就好像你在和父母打电话的时候,突然你女朋友打了进来,你果断接了女朋友的约会邀请,然后又重新接上父母这边的并续上刚刚的聊天内容
从RUNNABLE转到其他,上下文信息(聊天内容)需要先存储起来,然后再重新进入RUNABLE状态时,回复之前保存的线程的上下文信息(聊天内容),在保存和回复的过程就是上下文切换
在保存或恢复就是需要开销的,如CPU时间开销和CPU缓存内容失效等
你可以看看自己的Java程序运行时的上下文切换情况,我这边是win7,所以用了windows自带的perfmon
Linux也可以用perf命令查看
perf stat -e cpu-clock,task-clock,cs,cache-references,cache-misses java 你的程序名
线程监控
咱们需要把未知的东西变为已知的,黑盒变白盒
你可以尝试用JDK自身的jvisualvm监视
或是jmc也行
优劣
这个其实大家都基本了解,所以我不打算细讲来着
关于生命特征,可能就会涉及多种锁
死锁(Dead Lock):即都拥有自己的锁但是又在等对方的锁
T1(L1) 等 L2
T2(L2) 等 L1
活锁(Live Lock):一个线程一直尝试某个操作但是无果(就像部分暗恋、舔狗类似····,这个比喻是我和爱人说明后她给我的第一印象)
线程饥饿(Starvation):永远无法获得CPU执行机会,永远处于RUNNABLE状态的READY子状态。
相关术语
下次再说说,附录的synchronize和volatile。
我是MySelf,还在坚持学习技术与产品经理相关的知识,希望本文能给你带来新的知识点。