在 java 中,创建线程运行时代码的方式有 3 种:
- 继承 Thread 类,覆写其 run 方法;
- 实现 Runnable 接口,实现 run 方法,Thread 类也实现了 Runable 接口;
- 实现 Callable 接口,实现其 call 方法,这种方式是在 JDK1.5 中的 java 并发包中引入的;
一个很常见的错误是,把创建线程运行时代码和创建线程混为一谈。
在这里先给出一个结论:
创建线程的方式只有一种,就是创建 Thread 对象的实例,创建线程运行时代码就是以上的 3 种方式。
对于 Runnable 和 Callable 接口,主要就是为了实现接口中定义的方法,以便线程执行时回调,而实现的方法中的具体内容,就是我们所说的线程运行时代码。
对于 Thread,其本身是一个线程对象,不过由于其也实现了 Runnable 接口,因此其本身是将创建线程对象和线程运行时代码合为一体了。
一旦线程启动后 start 方法就会立即返回,而不会等待到 run 方法执行完毕才返回。
Callable 接口与 Runnable 接口类似,其定义了一个 call 方法,不同的是,它可以返回运行的结果。
public interface Callable<V> {
V call() throws Exception;
}
泛型参数 V 就是返回值的类型,Callable 接口需要与线程池结合使用。
选择创建Thread子类还是选择实现Runnable接口?
对于这两种方式哪种好并没有一个确定的答案,它们都能满足要求。
就我个人意见,我更倾向于实现Runnable接口这种方法。
因为 java 中有一个线程池的概念。所谓线程池,可以理解为有一堆线程对象已经创建好了,那么其缺的就是线程运行时代码。所以我们只需要提供了运行时代码就好了,因此实现 Runable 接口可能是更好的一种方式。
常见错误:启动线程时调用了 run 方法而非 start 方法
起初你并不会感觉有什么不妥,因为 run 方法确实被执行了。
但事实上,执行方法的是当前线程,并没有创建新线程。
start 方法才会启动一个新的线程,并分配相应的资源,例如栈内存空间,最后回调线程的运行时代码
线程名
当创建一个线程的时候,可以给线程起一个名字,它有助于我们区分不同的线程,帮助我们很好地排查问题。
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable, "New Thread");
thread.start();
System.out.println(thread.getName());
需要注意的是,因为 MyRunnable 并非 Thread 的子类,所以 MyRunnable 类并没有 getName 方法。可以通过以下方式得到当前线程的引用:
Thread.currentThread();
因此,通过如下代码可以得到当前线程的名字:
String threadName = Thread.currentThread().getName();