创建线程是并发编程中基础中的基础,在这之后才可以进行并发编程的内容。
总体大纲
第一种:实现 Runnable 接口
public class Demo1 implements Runnable {
@Override
public void run() {
System.out.println("用 Runnable 接口创建线程");
}
public static void main(String[] args) {
// 启动一个线程
new Thread(new Demo1()).start();
}
}
Runnable 源码为:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
因为 Runnable 是函数式接口,所以可以使用 Lambda 表达式简化:
// 启动一个线程
new Thread(() -> {
System.out.println("用 Runnable 接口创建线程 | Lambda 表达式");
}).start();
第二种:继承 Thread 类
public class Demo2 extends Thread {
@Override
public void run() {
System.out.println("继承 Thread 类创建线程");
}
public static void main(String[] args) {
// 启动一个线程
new Demo2().start();
}
}
第三种:实现 Callable 接口
这种创建线程的方式是有返回值的。
public class Demo3 implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("实现 Callable 接口创建线程,有返回值");
return "hello Callable";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 这个 FutureTask 就相当于 Runnable 和 Callable 的一个中间人
FutureTask<String> futureTask = new FutureTask<>(new Demo3());
// 创建一个线程
new Thread(futureTask).start();
// 获取返回值
System.out.println("Callable 接口的返回值为:" + futureTask.get());
}
}
Callable 源码为:
@FunctionalInterface
public interface Callable<V> {
/**
* 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;
}
因为 Runnable 是函数式接口,所以可以使用 Lambda 表达式简化:
// 这个 FutureTask 就相当于 Runnable 和 Callable 的一个中间人
FutureTask<String> futureTask1 = new FutureTask<>(() -> {
System.out.println("实现 Callable 接口创建线程,有返回值 | Lambda 表达式");
return "hello Callable";
});
// 创建一个线程
new Thread(futureTask1).start();
// 获取返回值
System.out.println("Callable 接口的返回值为:" + futureTask.get());
第四种:使用线程池
public class Demo4 {
public static void main(String[] args) {
// 创建一个线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
// 核心线程数
Runtime.getRuntime().availableProcessors(),
// 最大线程数
Runtime.getRuntime().availableProcessors() * 2 + 1,
// 存活时间
5,
// 时间单位
TimeUnit.SECONDS,
// 阻塞队列
new ArrayBlockingQueue<>(10),
// 创建线程的工厂
Executors.defaultThreadFactory(),
// 拒绝策略
new ThreadPoolExecutor.AbortPolicy());
// 创建一个线程
poolExecutor.execute(() -> {
System.out.println("线程池创建线程");
});
}
}
其实这里真正创建线程的核心是线程工厂。
Executors.defaultThreadFactory() 其实就是 new DefaultThreadFactory()
DefaultThreadFactory 类源码为:
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
通过 newThread() 这个方法我们可以发现,其实线程池创建线程还是 new 了一个 Thread 类。
实现 Runnable 接口比继承 Thread 类实现线程要好
(1)代码的架构考虑。实际上,Runnable 里只有一个 run() 方法,它定义了需要执行的内容,在这种情况下,实现了 Runnable 与 Thread 类的解耦,Thread 类负责线程启动和属性设置等内容,权责分明。
(2)在某些情况下可以提高性能。使用继承 Thread 类方式,每次执行一次任务,都需要新建一个独立的线程,执行完任务后线程走到生命周期的尽头被销毁,如果还想执行这个任务,就必须再新建一个继承了 Thread 类的类。如果我们使用实现 Runnable 接口的方式,就可以把任务直接传入线程池,使用一些固定的线程来完成任务,不需要每次新建销毁线程,大大降低了性能开销。
(3)Java 不支持多继承。如果我们的类一旦继承了 Thread 类,那么它后续就没有办法再继承其他的类,这样一来,如果未来这个类需要继承其他类实现一些功能上的拓展,它就没有办法做到了,相当于限制了代码未来的可拓展性。
综上所述,我们应该优先选择通过实现 Runnable 接口的方式来创建线程。