创建线程的四种方式及区别
1. 继承Thread类
一般步骤:
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
- 创建Thread子类的实例,即创建了线程对象
- 子类对象调用start()方法:启动线程,然后调用run()方法
代码示例
/**
* 定义子类并重写run方法
*/
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
// 创建子类对象,即创建了一个线程
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
// 子类对象调用start方法
t1.start();
t2.start();
// 以下操作在主线程中执行
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
这种创建方式代码简单,但因为Java是单继承一旦继承了Thread类就不能继承其他类
2. 实现Runnable接口
一般步骤:
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable接口实现类的实例,并以此实例作为
Thread(Runnable target)
的target来创建Thread对象,该Thread对象才是真正的线程对象。 - 调用线程对象的start()方法来启动线程。
代码示例
/**
* 定义Runnable接口实现类并重写run方法
*/
public class RunnableImpl implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
// 创建Runnable接口实现类对象:线程任务对象
RunnableImpl runnable = new RunnableImpl();
//创建Thread线程对象,构造方法中传递Runnable接口实现类对象
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
// 线程对象对象调用start方法
t1.start();
t2.start();
}
}
避免了单继承的局限性,可以继承其他类,多个线程可以共享同一个接口实现类的对象
3. 实现Callable接口
一般步骤:
- 定义Callable接口的实现类,并重写该接口的call()方法,该call()方法的方法体同样是该线程的线程执行体,需要注意的是call()方法有返回值,并且可以抛出异常
- 创建Callable接口实现类的对象,使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值,线程执行完成后可调用该对象的get()方法获取call()方法的返回值
- 使用FutureTask对象作为
Thread(Runnable target)
的target来创建Thread对象(因为FutureTask实现了Runnable接口) - 调用线程对象的start()方法来启动线程
- 如果想要获取线程执行完成后的返回值可调用FutureTask对象的get()方法
代码示例
/**
* 定义Callable接口实现类并重写call方法
*/
public class CallableImpl implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
return Thread.currentThread().getName() + "call()方法返回值";
}
}
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建Callable接口实现类的对象
CallableImpl callable = new CallableImpl();
// 使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
FutureTask<String> task1 = new FutureTask<String>(callable);
FutureTask<String> task2 = new FutureTask<String>(callable);
// 创建Thread类对象并调用start方法启动线程
new Thread(task1).start();
new Thread(task2).start();
// 获取call()方法的返回值,即线程运行结束后的返回值
String result1 = task1.get();
String result2 = task2.get();
System.out.println(result1);
System.out.println(result2);
}
}
实现Callable接口方式有返回值,可以抛出异常
4. 线程池方式
- 定义Callable接口的实现类,并重写该接口的call()方法,
- 调用Executors类中的newFixedThreadPool(int n)方法创建一个线程数量为n的线程池
- 调用线程池对象的submit()方法执行线程,返回执行结果Future对象
- 调用FutureTask对象的get()方法获取线程执行完成后的返回值
- 调用线程池对象的shutdown()方法关闭线程池
代码示例
public class MyStringCallable implements Callable<String[]> {
private String str;
public MyStringCallable(String str ){
this.str = str;
}
@Override
public String[] call() throws Exception {
return str.split(" +");
}
}
public class MySumCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int x = 1; x<= 100; x++){
sum+=x;
}
return sum;
}
}
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建有2个线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
//提交执行字符串切割任务
Future<String[]> futureString = executorService.submit(new MyStringCallable("aa bbb cc d e"));
System.out.println(Arrays.toString(futureString.get()));
//提交执行求和任务
Future<Integer> futureSum = executorService.submit(new MySumCallable());
System.out.println(futureSum.get());
executorService.shutdown();
}
}
解决了频繁创建线程的问题,降低资源消耗实现循环利用