一、什么是线程
在讨论什么是线程之前有必要说一下什么是进程,因为线程是进程中的一个实体,线程本身是不会独立存在的
进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。
线程则是进程中的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
二、线程的创建和运行
Jav当中有三种线程的创建方式,分别为实现 Rubnable 接口的 run 方法、继承 Thread 类并重写 run 方法、使用 FutureTask 方式创建。
首先看继承 Thread 类方式的实现:
/**
* @author Arley
* @date 2019/10/23 下午16:41
* @description 通过继承 Thread 类来实现线程
* 使用继承 Thread 类的方法来创建线程,多条线程之间无法共享线程类的实例变量
*/
public class MyThread extends Thread {
/**
* 重写 Run 方法, Run 方法的方法体就是线程执行体
*/
@Override
public void run() {
int i = 0;
int num = 100;
for (; i < num; i++) {
/**
* 调用 getName() 方法来返回当前线程名,可通过 setName(String name)为线程创建名字.
* 通过 this 获取当前线程
*/
System.out.println(this.getName() + " " + i);
}
}
public static void main(String[] args) {
int i = 0;
int num = 100;
for (; i < num; i++) {
/**
* 调用 Thread 类的 currentThread 方法获取当前线程
* 这个获取的是主线程 main 方法
*/
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
//创建启动 第一条线程
new MyThread().start();
//创建启动 第二条线程
new MyThread().start();
}
}
}
}
上面代码当中,MyThread 类继承了 Thread 类,并重写了 run 方法。在main方法种创建了两个 MyThread 的实例,调用 start 方法启动了 线程。
需要注意的是,当创建为 Thread 对象后线程并没有被启动执行,而是执行了 start 方法后才真正启动线程。
使用继承的好处是,在 run 方法种使用 this 就可以获取当前线程。
不好的地方是 Java 只支持单继承,如果继承了 Thread 类,就不可以在继承别的类。
另外任务与代码没有分离,多个线程执行一个任务需要多份代码。
实现 Runnable 接口的 run 方式实现:
实现 Runnable 的方式可以避免单继承带来的局限性。
/**
* @author Arley
* @date 2019/10/23 下午17:49
* @description 通过实现 Runnable 接口来实现线程
*/
public class RunnableTask implements Runnable {
@Override
public void run() {
int i = 0;
int num = 100;
for (; i < num; i++) {
/**
* 当线程类实现 Runnable 接口时候
* 如果想要获取当前线程,只能通过 Thread.currentThread 方法
*/
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
int num = 100;
for (int i = 0; i <= num; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
//主线程 main 方法
RunnableTask secondThread = new RunnableTask();
//通过 Thread(Target,name) 方法创建线程
new Thread(secondThread, "线程1").start();
new Thread(secondThread, "线程2").start();
}
}
}
}
上面代码中,两个线程共同使用一个 RunnableTask 逻辑,并且创建了两个线程,为每个线程添加参数(name)来进行任务区分,另外 RunnableTask 类还可以继承其他类。
以上说的两种创建 线程 的方式都有一个缺点,就是任务没有返回值,下面我们看最后一种
使用 FutureTask 方式创建线程:
/**
* @author Arley
* @date 2019/11/6 afternoon 17:09
* @description 使用 future task 方式创建线程,可以拿到任务的返回值
*/
public class CallerTCalbask implements Callable<String> {
/* 创建任务类,类似于Runnable */
@Override
public String call() throws Exception {
return "Hello";
}
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 (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
上面代码中 CallerTCalbask 实现了 Callable 接口的 call() 方法。
在 main 方法中创建了 FutureTask 对象(构造参数为 CallerTask 的实例),然后使用 futureTask.get() 等待任务执行完毕并返回结果。