theme: channing-cyan
这是我参与更文挑战的第20天,活动详情查看: 更文挑战。
前面两篇讲到了许多并发编程理论的内容,这一篇来谈谈基于
Java 8
如何创建并应用线程。惊呆了,为啥创建线程有且仅有一种方式呢?不是多个吗?
一、官方说
我们进入Thread
类源码,可以看到官方很清楚的说明了创建线程的方式。
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread.
翻译大致为创建线程的方式为两种方式,一种是继承Thread
类。
The other way to create a thread is to declare a class that implements the Runnable interface.
翻译大致为另一种创建线程的方式是实现Runnable
接口。
官方都实锤了两种创建线程的方式,为什么我还要一意孤行的说只有一种?
因为严格的讲,实现Runnable
接口的方式,#run
方法里面主要说明的还是线程要执行的任务是什么,真正启动线程还是用线程的#start
方法,因为实现的底层是#start0
这个native方法,我们是看不见的。有想了解的去看看源码吧! 所以事情的本质是:创建线程唯一的方式就是构造一个Thread
类,其它包括Runnable
、Callable
、Timer
、线程池
、Lambda
都只是核心实现了线程要执行的任务是什么。
二、创建线程和线程任务
2.1、Thread
通过看源码,Thread
的本质是实现了Runnable
的接口,代表了一个线程的实例。
启动线程的方式是调用start()
实例方法,start()
最终调用的是一个native
修饰的方法start0()
,代表线程的具体启动依赖于本地操作系统的函数。
private native void start0();
通过Thread
实现线程的方法很简单,继承它,重写run()
实例方法即可,。
实例代码如下:
public class CustomizeThread extends Thread{ @Override public void run() { System.out.println(Thread.currentThread().getName()+" of Thread running..."); } }
2.2、Runnable
Runable
是一个接口函数,因为Java
是接口多集成,类单继承。如果要实现的类已经继承了其它父类,可以采取实现Runnable
的形式来创建一个线程类。
实例代码如下:
public class CustomizeRunnable implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+" of Runnable running..."); } }
2.3、Callable
上面说到的Thread
和Runnable
线程执行完成后都没有返回值,如果确实需要一个返回值,可以实现Callable
接口,执行实例方法Call()
以后,主线程会得到一个返回值,然后主线程再进行相应的逻辑处理。
```
这个是一个泛型,V代表返回值的类型
@FunctionalInterface public interface Callable { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; } ```
实例代码如下:
public class CustomizeCallable implements Callable<String> { @Override public String call() throws Exception { final String threadName = Thread.currentThread().getName(); System.out.println(threadName+" of Callable running..."); return threadName; } }
不限于Callable
,ExecutorService
和Future
也能实现带返回值的线程。
2.4、Lambda
都Java 8
了,实际上我们不必继承类和实现接口去创建线程,实现多线程的运行。也可以尝试使用lambda
构造匿名的实现。因为Callable
和Runnable
都是接口函数,我们可以直接匿名的去实现线程执行的内部逻辑。
示例代码如下:
``` Runnable RUNNABLE_RUNNER = ()-> System.out.println(Thread.currentThread().getName()+" of Lambda Runnable running...");
Callable<String> CALLABLE_RUNNER = ()-> {
final String threadName = Thread.currentThread().getName();
System.out.println(threadName+" of Lambda Callable running...");
return threadName;
};
```
三、测试
测试我们采用启用一个线程池的方式去执行实例化的线程。
``` public interface ThreadTester {
ExecutorService executor = Executors.newFixedThreadPool(1);
static void testThread(){
final CustomizeThread customizeThread = new CustomizeThread();
executor.submit(customizeThread);
}
static void testRunnable(){
final CustomizeRunnable customizeRunnable = new CustomizeRunnable();
executor.submit(customizeRunnable);
}
static void testCallable() throws ExecutionException, InterruptedException {
final CustomizeCallable customizeCallable = new CustomizeCallable();
final Future<String> result = executor.submit(customizeCallable);
System.out.println("result is "+result.get());
}
static void testLambda() throws Exception {
CALLABLE_RUNNER.call();
RUNNABLE_RUNNER.run();
}
static void main(String[] args) throws ExecutionException, InterruptedException {
testThread();
testRunnable();
testCallable();
executor.shutdown();
testLambda();
}
``` 测试结果如下:
四、源码
代码内容并不复杂,但还是习惯性的附上源代码地址training.java.thread。
下面的一篇将会详细讲到线程的生命周期,这是并发编程很重要的基础内容,希望能带给大家一点不一样的领悟。
哥佬倌,莫慌到走!觉好留个赞,探讨上评论。欢迎关注面试专栏面时莫慌 | Java并发编程,面试加薪不用愁。也欢迎关注我,一定做一个长更的好男人。