创建线程的四种方式:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 使用Executors工具类创建线程池
1、继承Thread类
- 定义一个Thread类的子类,重写run方法,将相关逻辑实现,run()方法就是线程要执行的业务逻辑方法
- 创建自定义的线程子类对象
- 调用子类实例的star()方法来启动线程
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法正在执行...");
}
}
public class TheadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println(Thread.currentThread().getName() + " main()方法执行结束");
}
}
运行结果:
main main()方法执行结束
Thread-0 run()方法正在执行...
2、实现Runnable接口
- 定义Runnable接口实现类MyRunnable,并重写run()方法
- 创建MyRunnable实例myRunnable,以myRunnable作为target创建Thead对象,该Thread对象才是真正的线程对象
- 调用线程对象的start()方法
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
public class RunnableTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
}
}
运行结果:
main main()方法执行结束
Thread-0 run()方法正在执行...
3、实现Callable接口
- 创建实现Callable接口的类myCallable
- 以myCallable为参数创建FutureTask对象
- 将FutureTask作为参数创建Thread对象
- 调用线程对象的start()方法
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
System.out.println(Thread.currentThread().getName() + " call()方法执行中...");
return 1;
}
}
public class CallableTest {
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
try {
Thread.sleep(1000);
System.out.println("返回结果 " + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
}
}
执行结果:
Thread-0 call()方法执行中...
返回结果 1
main main()方法执行完成
什么是Callable和Future?
Callable接口类似于Runnable,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一点,被线程执行后,可以返回值,这个返回值可以被future拿到,也就是说Future可以拿到异步执行任务的返回值。
Future接口表示异步任务,是一个可能还没有完成的异步任务的结果。
所以Callable用于产生结果,Future用于获取结果。
4、使用 Executors 工具类创建线程池
Executors提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
主要有newFixedThreadPool,newCachedThreadPool,newSingleThreadExecutor,newScheduledThreadPool,后续详细介绍这四种线程池
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
public class SingleThreadExecutorTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
MyRunnable runnableTest = new MyRunnable();
for (int i = 0; i < 5; i++) {
executorService.execute(runnableTest);
}
System.out.println("线程任务开始执行");
executorService.shutdown();
}
}
运行结果:
线程任务开始执行
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
线程的run()和start()有什么区别?
- 每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,run()方法成为线程体,通过调用Thread类的start()方法来启动一个线程。
- start()方法用于启动线程,run()方法用于执行线程运行时的代码。run可以重复调用,而start()只能调用一次。
- start()方法来启动一个线程,真正实现了多线程运行。调用start()方法无需等待run()方法体代码执行完毕,可以直接继续执行其他的代码;此时线程处于就绪状态,并没有运行。然后通过次Thread类调用方法run来完成其运行状态,run方法运行结束,此线程中止,然后CPU在调度其他线程。
- run方法是在本线程中,只是线程的一个函数,而不是多线程。如果直接调用run方法,其实就是相当于调用另一个普通的函数而已,直接调用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()而不是run()方法。
总结:调用start()方法可启动线程并使线程进入就绪状态,而run方法只是thread的一个普通方法调用,还是在主线程中执行。