面试:为什么要使用多线程?
-
一个采用了多线程技术的应用程序可以更好地利用系统资源。其主要优势在于充分利用了CPU的空闲时间片,可以用尽可能少的时间来对用户的要求做出响应,使得进程的整体运行效率得到较大提高,同时增强了应用程序的灵活性。
-
更为重要的是,由于同一进程的所有线程是共享同一内存,所以不需要特殊的数据传送机制,不需要建立共享存储区或共享文件,从而使得不同任务之间的协调操作与运行、数据的交互、资源的分配等问题更加易于解决。
面试:线程和进程
-
进程:
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。 -
线程:
进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。 -
进程与线程的区别:
- 进程:有独立的内存空间,进程中数据的存放空间(堆空间和栈空间)是独立的,至少有一个线程,在java程序中一个进程至少拥有两个线程。
- 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
注意:
- 因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于 CPU 的调度,程序员是不能完全控制的(可以设置线程优先级)。而这也就造成的多线程的随机性。
- Java 程序的进程里面至少包含两个线程,主线程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个 线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
- 由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建 多线程,而不是创建多进程。
多线程的创建的五种方式
- 继承Thread类
public class 继承Thread类 {
public static void main(String[] args) {
new Demo1().start();
}
static class Demo1 extends Thread {
@Override
public void run() {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("name:" + name + ">>>"+i);
}
}
}
}
- 实现Runable接口
public class 实现Runnable接口 {
public static void main(String[] args) {
new Thread(new Demo1()).start();
}
static class Demo1 implements Runnable{
@Override
public void run(){
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("name:" + name + ">>>"+i);
}
}
}
}
建议使用哪种?建议使用Runable接口
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和数据独立。
- 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
- 匿名内部类
public class 匿名内部类 {
public static void main(String[] args) {
new Thread(() -> {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
System.out.println("name:" + name + ">>>"+i);
}
}).start();
}
}
- 实现juc包下的Callable接口
public class 实现Callable接口 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<String> res = executorService.submit(new Demo());
System.out.println("1111");
// res.get()会阻塞当前线程,等待submit执行完成
System.out.println(res.get());
System.out.println("2222");
// 线程池不会主动停止主线程的执行
// executorService.shutdown();
}
static class Demo implements Callable<String> {
@Override
public String call() throws Exception {
String name = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
Thread.sleep(200);
System.out.println("name:" + name + ">>>"+i);
}
return "ok";
}
}
}
- 线程池
…