想必大家对电脑都不陌生,不管你的电脑是Windows系统还是Mac系统,现在都已经是多任务操作系统,多任务就是可以在同一时间运行多个程序的能力。多任务带给我们的最直观的体验,就是我们可以边听音乐边打字。 不过多任务操作系统不一定要有多CPU,让多个任务分时共享CPU就好了。比如Windows多任务处理采用的是虚拟机技术。而且要注意的是单核的多任务操作系统在宏观上(看起来)是并行的,微观上(程序运行时)是并发的。(有空聊聊多核、单核、多CPU?)
在引入线程前我们先来了解下什么是进程,因为线程是进程中的一个实体,线程本身是不会独立存在的。
这里我们抽象的来理解下,我们可以将进程比作是一场正在进行的演唱会,而线程就是歌手、吉他手、鼓手等表演人员,台下的观众就是资源。
总结:进程是资源分配的基本单位,线程是CPU分配的基本单位。(也可以说是最小单位)
当然我们可以从更专业的角度来理解进程和线程之间的关系:(以下内容参考自《Java并发编程之美》、《深入理解Java虚拟机》)
在Java中,当我们启动main函数时,其实就是启动了一个JVM进程,而main函数所在的线程就是这个进程中的一个进程,也称主线程。
从上图我们可以看到,一个进程中可以有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程都有自己的程序计数器和栈区域。
常见的Java线程的3种创建方式:继承Thread类、实现Runnable接口、和Callable实现有返回值的线程。
public class Algorithm {
static int i=0;
static class MyThread extends Thread {
private String name;
public MyThread(){};
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
for(int k=0;k<=100;k++) {
System.out.println(name+" " +k);
}
}
}
public static void main(String[] args) {
MyThread thread1 = new MyThread("线程1");
MyThread thread2 = new MyThread("线程2");
thread1.start();
thread2.start();
}
}
从下图的执行结果中可以看出,线程的调度和先后是没关系的,而且线程1、2是交替执行的。
public class Run {
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("Hello");
}
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();
}
}
从上述代码我们可以看出,Runnable的实现依赖于Thread。当一个实现了Runnable的线程实例给Thread后,Threa会调用Runnable的run方法。
public class Call {
static class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
return "Hello";
}
}
public static void main(String[] args) {
FutureTask<String> futureTesk = new FutureTask(new MyCallable());
new Thread(futureTesk).start();
try{
String result = futureTesk.get();
System.out.println(result);
}catch (ExecutionException | InterruptedException e){
e.printStackTrace();
}
}
}
这是一个具有返回值的线程实现方式。
END