目录
继承 Tread 类,重写 run 方法
- 自定义一个 MyThread类,用来继承 Thread 类,重写 run 方法,最后在 main 方法中new 出 MyThread 实例,并调用该实例继承自父类 Thread 类的 start 方法,从而创建一个新线程
//先创建一个类 让该类继承 Thread 父类 class MyThread extends Thread { // 重写 run 方法 @Override public void run() { System.out.println("在 run方法中 自定义线程的工作内容"); } } public class ThreadDemo1 { public static void main(String[] args) { // 创建实例t Thread t = new MyThread(); // 启动并创建新线程 t.start(); // run 方法仅执行 run方法中的代码,并不会创建一个新线程 // t.run(); } }
注意:
- 创建实例并不会直接创建一个线程,而是调用到 start 方法才会创建一个新线程
- 一个进程至少含有一个线程,JVM 默认创建线程为 主线程(main),主线程(main)与 MyThread 创建出来的新线程为并发执行,同时执行,各执行各的
实现 Runnable 接口
- 自定义一个 MyRunnable 类,用来实现 Runnable 接口,重写 run 方法,最后在 main 方法中new 出 Thread 实例,并调用该实例的 start 方法,从而创建一个新线程
class MyRunnable implements Runnable { @Override public void run() { System.out.println("在 run方法中 自定义线程的工作内容"); } } public class ThreadDemo2 { public static void main(String[] args) { // 这只是描述了个任务 Runnable runnable = new MyRunnable(); // 把任务交给线程来执行 Thread t = new Thread(runnable); t.start(); } }
注意:
- 该方式能将 线程 和 线程要干的活 之间分离开,从而达到 解耦合 的效果
- 如果要改动代码,不用使用多线程,转而使用多进程、线程池等,此时代码的改动较小
使用匿名内部类,继承 Thread 类
- 直接创建 Thread 子类,同时实例化出一个对象,重写 run 方法,并调用该实例的 start 方法,从而创建一个新线程
public class ThreadDemo3 { public static void main(String[] args) { Thread t = new Thread() { @Override public void run() { System.out.println("在 run方法中 自定义线程的工作内容"); } }; t.start(); } }
注意:
使用匿名内部类,实现 Runable 接口
- 使用匿名内部类,实现 Runnable 接口作为 Thead 构造方法的参数,最后调用实例的 start 方法,从而创建一个新线程
public class ThreadDemo4 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("在 run方法中 自定义线程的工作内容"); } }); t.start(); } }
使用 Lambda 表达式
- lambda 本质为以一个匿名函数,()表示函数的形参,-> 为特殊语法,{ } 表示函数体,以上构成一个 lambda 表达式
public class ThreadDemo5 { public static void main(String[] args) { Thread t = new Thread(() -> { System.out.println("自定义线程的工作内容"); }); t.start(); } }
使用线程池创建线程
- ThreadPoolExecutor(线程池)的构造方法写起来很麻烦,但是标准库提供了一系列工程方法来简化其使用
- 工厂模式:将产品实例的权利移交给工厂,程序员不再通过 new 来创建所需对象,而是通过工厂获取所需产品,降低了产品使用者与使用者之间的 耦合关系
- 使用线程池不需显示的 new,而是通过 Executors 这个静态方法 new CaChedThreadPool 来完成
- 当然使用 submit 方法,将任务提交到线程池中,线程池中会有线程来完成这些任务
package Thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadDemo6 { public static void main(String[] args) { ExecutorService pool = Executors.newCachedThreadPool(); pool.submit( new Runnable() { @Override public void run() { System.out.println("在 run方法中 自定义线程的工作内容"); } }); } }
实现 Callable 接口
- 类似于 Runnable 一样
- Runnable 用来描述一个任务,描述的任务是 没有返回值 的
- Callable 也是用来描述一个任务,描述的任务是 有返回值 的
- 如果需要使用一个线程单独的计算出某个结果来,此时使用 Callable 是比较合适的
实例理解
- 创建线程计算 1 + 2 + 3 + ... + 1000,并返回结果
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class ThreadDemo30 { public static void main(String[] args) throws ExecutionException, InterruptedException { // 使用 Callable 来计算 1 + 2 + 3 + ... + 1000 Callable<Integer> callable = new Callable<Integer>() { @Override // 相当于 Runnable 的 run 方法,run 方法返回值是 void // 此处的 call 方法返回值是 泛型参数 public Integer call() throws Exception { int sum = 0; for (int i = 0; i <= 1000; i++) { sum += i; } return sum; } }; FutureTask<Integer> futureTask = new FutureTask<>(callable); Thread t = new Thread(futureTask); t.start(); // get 方法就是获取结果 // get 会发生阻塞,直到 callable 执行完毕,get 才阻塞完成,才获取到结果 Integer result = futureTask.get(); System.out.println(result); } }
- 上述代码中,callable 不能直接传入到 Thread 的构造方法里,需要套上一层其他的辅助类 FutureTask
- Thread 类的构造方法接受的是一个实现了 Runnable 接口的对象,而不是 Callable 接口的对象
- 为了能在 Thread 中执行 Callable 任务,需要使用一个中介类来桥接它们之间的差异,即 FutureTask 类
- FutureTask 实现了 Runnable 接口,同时也实现了 Future 接口,因此可以将 Callable 任务封装起来,使其具备了 Runnable 的特性,可以在后台线程中执行
- 它还提供了获取计算结果的方法,如 get 方法
运行结果