线程的实现方式共有四种,第一种是继承Thread抽象类;第二种是实现Runnable接口;第三种是实现Callable接口;最后一种是通过线程池创建线程。
1.继承Thread类
写一个类去继承Thread类,重写其中的run()方法,然后调用start()方法启动线程。
2.实现Runnable接口
写一个类去实现Runnable接口,重写其中的run()方法;用用实现Runnable接口的对象作为参数实例化一个Thread对象,调用Thread类的start()方法来启动线程。
实现Runnable接口比继承Thread类所具有的优势:
1. 适合多个相同的程序代码的线程去共享同一个资源。 2. 可以避免java中的单继承的局限性。 3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。 4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
注意:
Thread类本质上也是实现了Runnable接口,启动线程的唯一方法就是通过Thread类的start()方法,run方法只是一个方法体。
start()方法是一个native(本地)方法,它将启动一个新线程,并执行run()方法(Thread类中提供的run()方法是一个空方法)。
然后我们再看看多线程的执行问题。
由图可以看出,输出的值是交替的无顺序的,原因分析:
调用start()方法后并不是立即执行多线程代码,而是使得该线程变为可运行状态,什么时候调度运行由操作系统决定的。
3.实现Callable接口
实现Callable接口,重写call()方法来实现一个线程。
Callable接口与Runnable接口的功能类似,但提供了比Runnable更强大的功能,主要表现为以下三点:
(1)Callable可以在任务结束后提供一个返回值,Runnable无法提供这个功能;
(2)Callable接口中的call()方法可以抛出异常,而Runnable的run()方法不能抛出异常,必须在run()捕获;
(3)运行Callable可以拿到一个Future对象,Future对象表示异步计算的结果,它提供了检查计算是否完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下,就可以使用Future来监视目标线程调用call()方法的情况,当调用Future的get方法以获取结果时,当前线程就会阻塞,直到call()方法结束返回结果。
4.通过线程池创建线程
线程池可以自动创建也可以手动创建,自动创建体现在Executors工具类中,常见的可以创建newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool;手动创建体现在可以灵活设置线程池的各个参数,体现在代码中即ThreadPoolExecutor类构造器上各个实参的不同:
public static ExecutorService newFixedThreadPool(int var0) {
return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}
public static ExecutorService newSingleThreadExecutor() {
return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
}
public static ScheduledExecutorService newScheduledThreadPool(int var0) {
return new ScheduledThreadPoolExecutor(var0);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {……}
总结:
前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果
后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中