多线程详解
一、什么是线程,进程
线程
是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
进程
是指在系统中正在运行的一个应用程序,程序一旦运行就是进程。进程是系统进行资源分配的独立实体, 且每个进程拥有独立的地址空间。
二者的关系
每个进程可以包含多个线程(一对多),每条线程只属于单独的一个进程(一对一)。
二、什么是串行,并行
串行
按照某种顺序依次不间断执行某项任务叫做串行。
并行
同时执行多项任务叫并行。
三、为什么要使用多线程
多线程的好处:
使用多线程可以把程序中占据时间长的任务放到后台去处理,如图片、视屏的下载发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好
多线程的缺点:
大量的线程降低代码的可读性;
更多的线程需要更多的内存空间;
当多个线程对同一个资源出现争夺时候要注意线程安全的问题。
四、如何创建线程
方式一
直接使用 Thread t = new Thread() 方法并实现匿名内部类的方式去创建线程对象。
public class MyTest {
/**
* 引用日志
*/
private static Logger logger = LoggerFactory.getLogger(MyTest.class);
public static void main(String[] args) {
// 复合的创建线程(包含执行的任务代码)
Thread t = new Thread(){
@Override
public void run(){
logger.info("running");
}
};
// 给线程t一个名字
t.setName("dst");
// 线程t启动
t.start();
logger.info("running");
}
}
执行结果:
[frame] 2021-04-22 11:15:15,205 - 0 INFO [dst] mumu_1.MyTest$1:23 - running
[frame] 2021-04-22 11:15:15,205 - 0 INFO [main] mumu_1.MyTest:31 - running
Process finished with exit code 0
这种方式是将线程和线程要执行的业务代码耦合在一起。在实际工作中不建议使用这种方式。
方式二
先创建一个Runnable对象,并实现它的抽象方法。再创建一个线程接收这个Runnable对象再执行线程。
/**
* 引用日志
*/
private static Logger logger = LoggerFactory.getLogger(MyTest.class);
public static void main(String[] args) {
// 构建runnable对象(任务)
Runnable runnable = new Runnable() {
public void run() {
logger.info("running");
}
};
// 构建线程对象(执行者)
Thread thread = new Thread(runnable);
// 线程起名字
thread.setName("dst");
// 启动线程
thread.start();
logger.info("running");
}
}
执行结果
[thread] 2021-04-22 12:18:17,289 - 0 INFO [dst] mumu_1.MtTest$1:21 - running
[thread] 2021-04-22 12:18:17,289 - 0 INFO [main] mumu_1.MtTest:30 - running
Process finished with exit code 0
这种方式会将任务和线程分开,从而比较灵活。
方式三
使用Callable 和 FutureTask 的组合从而创建带有返回值的任务对象。
再使用Thread的构造方法,start方法执行任务。
public class MmTest {
/**
* 引用日志
*/
private static Logger logger = LoggerFactory.getLogger(MyTest.class);
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建Callable 对象
Callable<Integer> callable = new Callable() {
// 匿名内部类
public Integer call() throws Exception {
logger.info("running");
Thread.sleep(10000);
return 100;
}
};
FutureTask<Integer> task = new FutureTask(callable);
Thread thread = new Thread(task,"dst");
thread.start();
// task.get()会阻塞,等待线程dst执行结束,并返回结果
logger.info("{}",task.get());
}
}
执行结果
[thread] 2021-04-22 12:41:08,127 - 0 INFO [dst] mumu_1.MmTest$1:26 - running
[thread] 2021-04-22 12:41:18,160 - 10033 INFO [main] mumu_1.MmTest:38 - 100
Process finished with exit code 0
这种使用线程的方式,主要用在线程内部执行的任务有返回值需要用返回值与其他线程通信。
五、如何查看线程的相关信息
windows系统
查看进程
可以同时按:Ctrl+Alt+Delete 三个按键,打开 任务管理器 查看进程,以及对应的进程。
image.png
可以按:windows+R ,然后输入 CMD 打开命令行窗口。输入:tasklist | findstr "java"
image.png
就可以找到正在运行的java程序以及它的pid
如果需要精准定位运行的程序名,建议使用jdk自带的命令:jps 命令
杀死线程
命令:taskkill /f /pid + pid 即可杀死对应pid的进程
image.png
linux系统
查看进程
命令:ps -ef | grep "java" 查看java线程
命令:top 查看实时的进程相关信息
命令:top -H(代表要查看线程) -p(代表筛选pid) + pid
杀死线程
kill + pid
JDK自带命令
前提:机器上必须安装虚拟机(JDK)
命令:jps 查看所有的java进程
image.png
命令:jstack + pid 抓取这一时刻的运行的java线程的快照信息
JDK自带的图形化工具:jconsole
image.png
image.png
六、应用总结:
异步调用:主线程运行期间,其他线程异步执行其他耗时操作。
提高效率:并行计算,速断计算时间
同步等待:join方法
统筹规划:合理使用线程达到最优效果
网友评论