01 概述
(1)线程与进程
(2)并行与并发
- 并发:同一时间段同时执行(微观串行,宏观并行)
- 并行:同一时间同时执行
(3)应用
【1】异步调用
- 异步:不需要等待结果返回就能继续执行
- 同步:需要等待结果返回才能继续执行(多线程中同步还有线程步调一致的含义)
- 作用:避免一些IO操作阻塞主线程,用新线程进行IO操作
public class Async {
public static void main(String[] args) {
// 同步等待
// IO操作代码,如文件读入或写出
// IO操作在主线程中,需等待IO操作执行结束才可继续执行
System.out.println("做其他事");
// 异步不等待
new Thread(
// IO操作代码,如文件读入或写出
).start();
// IO操作放在新线程中,主线程不需要等待IO操作结果返回即可继续执行
System.out.println("做其他事");
}
}
【2】提高效率
- 举例:用3个线程执行3个计算,主线程汇总该3个线程的计算结果
- 单核CPU:3个线程交替执行,时间较长
- 多核CPU:3个线程并行,时间更短
- 结论:
- 单核CPU使用多线程是避免一个线程一直占用CPU,并不能实际提高运行效率
- 利用多核CPU并行优势,拆分任务并行执行,提高效率。(看情况)
- 待补充:非阻塞IO与异步IO优化
02 Java线程
(1)创建与运行线程
【1】使用Thread
// 创建线程对象
Thread t = new Thread("t1") {
// 重写run方法,线程执行的run方法中代码
@Override
public void run() {
// 执行的任务
}
};
// 启动线程
t.start();
【2】Runnable配合Thread
- 线程和任务分开:Thread代表线程,Runnable代表任务(线程执行的代码)
- Runnable更方便与线程池等高级API配合
// 写法1:
Runnable runnable = new Runnable() {
@Override
public void run() {
// 要执行的任务代码
log.debug(Thread.currentThread().getName() + "执行任务");
}
};
// 创建线程对象: 参数1:任务对象;参数2:线程名
Thread t = new Thread(runnable, "t1");
// 启动线程
t.start();
// 写法2:lambda
Runnable task1 = () -> System.out.println(Thread.currentThread().getName());
Thread t1 = new Thread(task1, "t2");
t1.start();
// 写法3:
Thread t2 = new Thread(() -> System.out.println(Thread.currentThread().getName()), "t3");
t2.start();
【1-2】原理:Thread与Runnable的关系
-
Thread类实现Runnable接口
//源码中 Thread类中的run方法,target是实现了Runnable接口的一个对象 @Override public void run() { if (target != null) { target.run(); } }
【3】FutureTask配合Thread
- FutureTask可接收Callable类型的参数(重写call方法),用来处理有返回结果的情况
- FutureTask实现Runnable接口,run方法中会执行call方法,Callable类型是函数式接口
// 创建任务1
FutureTask<Integer> task1 = new FutureTask<>(() -> {
System.out.println("hello");
return 100;
});
// 创建任务2
FutureTask<Integer> task2 = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 123;
}
});
// 创建线程
Thread t = new Thread(task1, "t1");
t.start();
// 主线程阻塞,同步等待task执行完毕的结果
Integer result1 = task1.get();
System.out.println("result1 = " + result1);
Integer result2 = task2.get();
System.out.println("result2 = " + result2);
(2)查看线程
- Windows:
- 任务管理器
tasklist
查看进程,taskkil
l杀死线程
- Linux:
ps -ef
:查看所有进程--ps -ef | grep java
ps -fT -p <PID>
:查看某个进程的所有线程kill <PID>
:杀死线程top
:按大小H切换是否显示线程top -H -p <PID>
:查看某个进程所有线程
- Java:
jps
:查看所有java进程jstack <pid>
:查看某个java进程的所有进程状态- jconsole查看某个java进程中线程的运行情况(图形界面),可远程监控(配置百度)
(3)线程切换
- 线程上下文切换的原因:
- 线程CPU时间片用完
- 垃圾回收
- 有更高优先级的线程需运行
- 线程自己调用sleep、yield、wait、join、park、sychronized、lock等方法
- Java线程状态包含程序计数器、虚拟机栈中每个栈帧的信息(局部变量、操作数栈、返回地址)等