查看进程和线程
Windows:
1.任务管理器直接查看
2.命令行tasklist查看,同时taskkill杀死进程/F是强制杀死,可以加一个findstr java缩小查找范围,jps命令只看java进程(jdk自带)
linux
ps -fe 查看所有进程grep java可以进行筛选
kill+PID杀死进程
top 显示系统中进程的动态实时视图
top -H -p 查看某个进程(PID)的所有线程
Java
jps 命令查看所有 Java 进程
jstack 查看某个 Java 进程(PID)的所有线程状态
jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)
线程运行的原理
栈和栈帧
栈内存给线程使用,线程启动后,虚拟机会给他分配一块栈内存,栈由多个栈帧组成,对应着每次方法调用所占用的内存,每个线程只能有一个活动栈帧。栈内存方法执行完就会被释放,执行的时候会记录返回地址,执行完了之后就会回到返回地址去执行。程序计数器中记录了该执行哪行代码。栈帧中记录信息:局部变量表、返回地址、锁记录、操作数栈。(注意debug的时候要选择thread)每个线程的栈帧互不干扰。
线程上下文切换
通俗说就是使用CPU的线程发生了切换。
1.时间片用完
2.垃圾回收,会暂停当前的所有工作线程
3.有更高优先级的线程需要运行
4.线程自己调用了sleep、yield、wait、join、park、synchronized、lock方法
发生线程切换时,操作系统保存当前线程的状态(包括程序计数器、每个栈帧的信息(如局部变量、操作数栈、返回地址等)),并恢复另一个线程的状态,Java对应的是程序计数器,记录下一条jvm指令的地址,是线程私有的。频繁的上下文切换会影响性能。
常见方法
start和run
直接调用run方法相当于主线程自己去执行,还是单线程
public static void main(String[] args) {
Thread t1 = new Thread("t1") {
@Override
public void run() {
log.debug(Thread.currentThread().getName());
FileReader.read(Constants.MP4_FULL_PATH);
}
};
t1.run();
log.debug("do other things ...");
}
输出:只有main线程
19:39:14 [main] c.TestStart - main
19:39:14 [main] c.FileReader - read [1.mp4] start ...
19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms
19:39:18 [main] c.TestStart - do other things ...
改成start之后
19:41:30 [main] c.TestStart - do other things ...
19:41:30 [t1] c.TestStart - t1
19:41:30 [t1] c.FileReader - read [1.mp4] start ...
19:41:35 [t1] c.FileReader - read [1.mp4] end ... cost: 4542 ms
sleep和yield
sleep使得线程从Running进入Timed Waiting状态(放弃时间片的使用),其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException(其实这也是为啥在写sleep的时候,一定要加异常处理的原因),睡眠结束后的线程未必会立刻得到执行,建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性(有一个时间的单位)
打断示例
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread("t1"){
@Override
public void run(){
log.debug("enter sleep....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
log.debug("wake up....");
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(1000);
log.debug("interrupt....");
t1.interrupt();
}
yield让线程从Running进入Runnable就绪状态,让出CPU给其他线程,但是这是由操作系统决定的,不一定能让出去
线程优先级
1-10,默认是5数字越大表示越高,提示调度器优先调度,仅仅是一个提示(一般没什么卵用),CPU比较忙的时候可能会有用,CPU比较闲的时候,几乎没用。
案例:防止CPU占用100%
sleep实现,没有利用CPU的时候,不能while(true)空转,用yield或者sleep让出使用权,适用于无需锁同步的场景
while(true){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
join(join后还可以加时间)
这里的结果竟然是0
static int r = 0;
public static void main(String[] args) throws InterruptedException {
test1();
}
private static void test1() throws InterruptedException {
log.debug("开始");
Thread t1 = new Thread(() -> {
log.debug("开始");
try {
sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("结束");
r = 10;
});
t1.start();
log.debug("结果为:{}", r);
log.debug("结束");
}
21:40:50 [main] c.test8 - 开始
21:40:50 [Thread-0] c.test8 - 开始
21:40:50 [main] c.test8 - 结果为:0
21:40:50 [main] c.test8 - 结束
21:40:50 [Thread-0] c.test8 - 结束
主线程和t1并行执行,t1需要1s才能将10赋给r,而主线程一开始就要打印r的结果,这里用sleep就不那么合适了。在start之后加个join即可解决问题。
join同步示例
static int r1 = 0;
static int r2 = 0;
private static void test2() throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
r1 = 10;
});
Thread t2 = new Thread(() -> {
try {
sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
r2 = 20;
});
long start = System.currentTimeMillis();
t1.start();
t2.start();
t1.join();
t2.join();
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
interrupt方法
打断sleep,wait,join等阻塞状态下的线程
示例代码:
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(()->{
log.debug("sleep...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
log.debug("打断标记:{}",t1.isInterrupted());
}
打断正在运行的线程(其实还是由被打断的线程自己决定的)
示例代码
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(()->{
while(true){
//这里就是,如果被打断了就结束
boolean interrupt =Thread.currentThread().isInterrupted();
if(interrupt){
log.debug("被打断了,退出循环");
break;
}
}
},"t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
}