1. 概念
进程:进程是系统分配资源的最小单位,可以简单地认为一个进程就是一个运行的程序,任务管理器中的每一个在运行的任务都是一个进程。
线程:线程是进程调度的最小单元,每一个进程至少有一个线程,也可以一个进程有多个线程。
多进程:操作系统同时运行多个任务,但是感官上并没有中断的效果,实际上操作系统还是将时间划分为若干个时间段(非常短),每个时间段执行不同的任务。单核CPU的情况下每个任务按照串行的方式交替执行,多核CPU的情况下多进程可以采用并行的方式同时执行。
多线程:由于多进程管理方式太过于笨重让CPU在多个应用之间来回切换很多时候可能导致效率不高,因此引入多线程的概念。如果一个进程有多个小任务,而这每一部分任务之间并没有严格的先后执行顺序,当一个小任务执行遇到障碍时可以让CPU去执行当前任务下的其他小任务,而不是将当前好不容易分配到CPU的使用时间丢弃将整个进程挂起转到执行其他程序。即让CPU在当前程序的小任务之间切换而不是在程序之间切换。
多线程 vs 多进程:
- 同一个进程下的线程之间共享数据
- 线程之前通讯更方便
- 线程比进程更容易切换
- 线程相比进程更容易管理
2. Java创建多线程的方式
- 通过继承Thread类来实现多线程,Thread类是实现了Runnable接口的类,最后直接实例化类就可以实现多线程了。
public class TestThread extends Thread {
@Override
public void run() {
for(int i = 0;i < 200;i++) {
System.out.println("run: " + i);
}
}
public static void main(String[] args) {
TestThread t = new TestThread();
t.start(); //开辟子线程和父线程一起跑
for(int i = 0;i < 200;i++){
System.out.println("Main: "+i);
}
}
}
- 实现Runnable对象线程化接口,Runnable是Java的四大主要接口之一其余三个是:Clonable用于对象克隆化,Comparable用于对象比较,Serializable用于对象序列化。Runnable实现多线程不一样,实现Runnable的对象需要包装在Thread类中才能启动。
public class TestRunnable implements Runnable {
@Override
public void run() {
for(int i = 0;i < 200;i++) {
System.out.println("run: " + i);
}
}
public static void main(String[] args) {
TestRunnable t = new TestRunnable();
new Thread(t).start(); //new一个Thread的外壳依托
for(int i = 0;i < 200;i++){
System.out.println("Main: "+i);
}
}
}
- 无论是上面那种方法实现多线程都必须重写run方法,并且调用start方法启动多线程。
- 其中start方法调用时会自动的启动run方法(并行执行)。如果直接调用run方法就变成了普通的类方法访问(串行执行)。
- 同一个线程只能start一次,多次start启动会报错。
- 当启动多个线程之后,它们线程之间的执行顺序是随机的,具体的执行顺序操作系统说了算。而且每一次启动程序得到结果基本上不会相同。
- 线程无需关闭,执行完run方法之后自动结束这个线程的生命周期就截止了。
- main函数(线程)可能早于其他的线程,但是整个程序不会结束,必须等所有的线程结束之后程序才会结束。
3. 多线程实现对比
-
由于Java是一门单继承类,多实现接口的面向对象语言,所以很多时候想要继承的类可能不够,如果继承了Thread类就无法继承其他的类,这样会占用父类的名额,并且Thread类也是实现了Runnable接口的。从这个角度看继承 Thread类 不如实现 Runnable接口 方便。
-
继承Thread类的类可以直接实例化对象来启动线程,而实现Runnable接口的类还必须依托在Thread类上才能启动线程。从这个角度上看Runnable接口的确不如Thread类。
-
Runnable接口 比 Thread类 更容易实现多线程中资源共享,Thread中需要采用静态变量,而Runnable中普通变量即可。
-
Runnable接口可以采用一份资源多份代理的形式,而Thread类不行。
结论:建议实现Runnable接口来实现多线程,除了第二点有略微的劣势其他情况下Runnable优势还是多多,特别是针对Java单继承的短板。