创建线程的四种方式
1、通过继承Thread类
2、通过实现Runnable接口
3、通过实现Callable接口
4、通过使用Executors工具类创建线程池
Thread
要继承Thread类实现创建线程,主要有以下步骤:
1、定义一个Thread子类MyThread,重写run()方法,实现相关逻辑,run()方法就是线程要执行的业务逻辑方法
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法正在执
行...");
}
}
2、创建自定义的线程子对象,比如MyThread的对象
3、调用子类MyThread实例的start()方法来启动线程
public class ThreadTest{
public static void main(String[] args){
MyThread myThread = new MyThread();
myThread.start();
System.out.println(Thread.currentThread().getName() + "main()方法执行结束");
}
}
运行结果
main main()方法执行结束
Thread-0 run()方法正在执行...
Runnable
实现Runnable接口创建线程的步骤如下:
1、定义Runnable接口实现类MyRunnable,重写run()方法
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
2、创建MyRunnable实例,以此实例作为target创建Thread对象,该Thread对象才是真正的线程对象
3、调用线程对象的start()方法
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()方法执行中...
Callable
什么是 Callable 和 Future?
Callable 接口类似于 Runnable,从名字就可以看出来了,但是 Runnable 不会 返回结果,并且无法抛出返回结果的异常,而 Callable 功能更强大一些,被线 程执行后,可以返回值,这个返回值可以被 Future 拿到,也就是说,Future 可以拿到异步执行任务的返回值。 Future 接口表示异步任务,是一个可能还没有完成的异步任务的结果。所以说 Callable用于产生结果,Future 用于获取结果。
什么是 FutureTask
FutureTask 表示一个异步运算的任务。FutureTask 里面可以传入一个 Callable 的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判 断是否已经完成、取消任务等操作。只有当运算完成的时候结果才能取回,如果 运算尚未完成 get 方法将会阻塞。一个 FutureTask 对象可以对调用了 Callable 和 Runnable 的对象进行包装,由于 FutureTask 也是Runnable 接口 的实现类,所以 FutureTask 也可以放入线程池中。
实现Callable接口创建线程步骤如下:
1、创建实现Callable接口的类myCallable
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
System.out.println(Thread.currentThread().getName() + " call()方法执行中...");
return 1;
}
}
2、以myCallable为参数创建FutureTask对象
3、将FutureTask作为参数创建Thread对象
4、调用线程对象的start()方法
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()方法执行完成");
}
}
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 run()方法执行中...
pool‐1‐thread‐1 run()方法执行中...
pool‐1‐thread‐1 run()方法执行中...
pool‐1‐thread‐1 run()方法执行中...
pool‐1‐thread‐1 run()方法执行中...
Runnable和Callable的区别
相同点:
- 都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动线程
主要区别 :
- Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,是个泛 型,和Future、FutureTask配合可以用来获取异步执行的结果
- Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息
注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法 会阻塞主进程的继续往下执行,如果不调用不会阻塞。
线程的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() 方
那么,为什么我们调用start()方法后会执行run()方法呢?为什么不直接调用run呢?
new 一个 Thread,线程进入了新建状态。调用 start() 方法,会启动一个线程 并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执 行,并不会在某个线程中执行它,所以这并不是多线程工作。 总结: 调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。