想要知道什么是多线程,那么我们先了解一下什么是进程。
1.1 什么是线程?
线程其实是进程中的一个实体,线程是不能单独存在的。进程则是代码在数据集合上的一次运行活动,
简单来说就是系统进行资源分配和调度的基本单位。线程则是进程的一个执行路径,一个进程中至少有一个
线程,进程中的多个线程共享进程的资源。
1.2 线程的创建及运行
Java创建线程的方式有三种,分别为实现Runnable接口的run方法,继承Thread类并重写run方法,
还有使用FutureTask的方式。
- 第一种: 继承Thread类的实现方式
运行结果:// 继承Thread类的方式 public static class MyThread extends Thread { @Override public void run() { System.out.println("继承Thread类的方式."); } } public static void main(String[] args) { // 创建线程 MyThread myThread = new MyThread(); // 启动线程 myThread.start(); }
注意: 当创建完Thread对象后,该线程并没有被启动执行,而是调用了start方法后才真正启动线程。但是,start方法只是让线程处于就绪状态,需要获取CPU资源后才会真正的处于运行状态,而一旦run方法执行完毕,该线程就处于终止状态。
优点: 使用继承的方式好处在于可以直接在run()方法内使用this获取当前线程,而无需再使用Thread.currentThread()方法。
缺点: Java不支持多继承,如果继承了Thread类就无法继承其他类,另外任务与代码没有分离,当多个线程执行一样的任务时就需要多份代码。而Runable就没有这个限制。
- 第二种: 重写Runnable接口的run的方式
// 实现Runable接口
public static class MyRunable implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable接口的方式");
}
}
public static void main(String[] args) {
MyRunable mr = new MyRunable();
new Thread(mr).start();
new Thread(mr).start();
}
运行结果:
以上两个线程都使用了同一个mr代码逻辑,另外,MyRunable可以继承其他类,但是这两种方式都有同一个缺点,就是没有返回值。 下面再介绍最后一种,即FutureTask的方式。
- 第三种: 使用FutureTask的方式
// FutureTask的方式
public static class CallerTask implements Callable<String> {
@Override
public String call() throws Exception {
return "FutureTask的创建方式";
}
}
public static void main(String[] args) {
// 创建异步任务
FutureTask<String> futureTask = new FutureTask<String>(new CallerTask());
// 启动线程
new Thread(futureTask).start();
try{
// 等待执行完毕并返回结果
String result = futureTask.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
运行结果:
上面代码中的CallerTask实现了Callable接口的call()方法,并使用创建的FutureTask对象作为任务,创建了一个线程,最后通过futureTask.get()拿到返回结果。
总结
使用继承的方式好处是方便传参。而使用Runnable接口的方式,则只能在主线程里面被声明为final的变量。前两中方式都没办法拿到任务的返回结果,但是FutureTask的方式就可以。