实现多线程的4种方式
使用实现多线程有四种方式:
-
继承 Thread 类;
-
实现 Runnable 接口;
-
使用 Callable 和 FutureTask 实现有返回值的多线程;
-
使用 ExecutorService 和 Executors 工具类实现线程池(如果需要线程的返回值,需要在线程中实现 Callable
和 Future 接口)
1.1 继承 Thread 类
Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过
Thread 类的 start() 实例方法,start() 方法是一个 native 方法,它将启动一个新线程,并执行 run() 方法。
继承 Thread 类的优点:简单,且只需要实现父类的 run 方法即可( start 方法中含有 run 方法,会创建一个新的
线程,而 run 是执行当前线程)。
继承 Thread 类的缺点:Java 的单继承,如果对象已经继承了其他的类则不能使用该方法,且不能获取线程的返回
值。
package com.multithreading;public class ThreadDemo {public static void main(String[] args) {MyThread1 myThread1 = new MyThread1();MyThread1 myThread2 = new MyThread1();myThread1.start();myThread2.start();}}class MyThread1 extends Thread {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}
# 程序输出
Thread-1
Thread-0
1.2 实现 Runnable 接口
实现 Runnable 接口优点:简单,实现 Runnable 接口必须实现 run 方法。
实现 Runnable 接口缺点:创建一个线程就必须创建一个 Runnable 的实现类,且不能获取线程的返回值。
package com.multithreading;public class RunnableDemo {public static void main(String[] args) {System.out.println(Thread.currentThread().getName());Thread t1 = new Thread(new MyThread2());t1.start();}
}class MyThread2 implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}
# 程序输出
main
Thread-0
1.3 使用 Callable 和 FutureTask
CallabTask 优点:可以获取多线程的返回值。
CallabTask 缺点:每个多线程都需要创建一个 Callable 的实现类。
package com.multithreading;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class CallableDemo {public static void main(String[] args) {FutureTask<String> futureTask = new FutureTask<>(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();}}
}class CallerTask implements Callable<String> {@Overridepublic String call() {return Thread.currentThread().getName();}
}
# 程序输出
Thread-0
1.4 使用 ExecutorService 和 Executors
线程池 ExecutorService 和工具类 Executors 优点:可以根据实际情况创建线程数量,且只需要创建一个线程池即
可,也能够通过 Callable 和 Future 接口得到线程的返回值,程序的执行时间与线程的数量紧密相关。
线程池 ExecutorService 和工具类 Executors 缺点:需要手动销毁该线程池(调用shutdown方法)。
package com.multithreading;import java.util.concurrent.Callable;public class MyTask implements Callable<String> {private final int id;public MyTask(int id) {this.id = id;}@Overridepublic String call() {return "result of TaskWithResult: " + id;}
}
package com.multithreading;import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class ExecutorServiceDemo {static final int NUMBER = 10;public static void main(String[] args) {ExecutorService exec = Executors.newCachedThreadPool();ArrayList<Future<String>> results = new ArrayList<>();for (int i = 0; i < NUMBER; i++) {results.add(exec.submit(new MyTask(i)));}exec.shutdown();for (Future<String> future : results) {try {System.out.println(future.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}}
}
# 程序输出
result of TaskWithResult: 0
result of TaskWithResult: 1
result of TaskWithResult: 2
result of TaskWithResult: 3
result of TaskWithResult: 4
result of TaskWithResult: 5
result of TaskWithResult: 6
result of TaskWithResult: 7
result of TaskWithResult: 8
result of TaskWithResult: 9