一、创建线程
- 每个java程序在启动时,都已经创建了一个线程,这个线程就是main线程。
- 如果需要在main线程之外再创建线程,则可以通过多种方式创建线程。
1.方法一,直接使用Thread
// 创建线程对象
// 构造方法的参数是给线程指定名字
Thread t1 = new Thread("t1") {
@Override
// run 方法内实现了要执行的任务
public void run() {
log.debug("hello");
}
};
// 启动线程
t1.start();
// 使用lambda表达式简写
Thread t2 = new Thread(() -> {
log.debug("running");
}, "t2");
t2.start();
2.方法二,使用 Runnable 配合 Thread
把【线程】和【任务】(要执行的代码)分开
- Thread 代表线程
- Runnable 可运行的任务(线程要执行的代码)
public static void main(String[] args) {
Runnable r = () -> {
log.debug("running");
};
Thread t = new Thread(r, "t");
t.start();
}
- 原理之 Thread 与 Runnable 之间的关系
- 继承 Thread 类创建线程,把线程和任务合并在一起了;实现Runnable接口,把线程和任务分开了。
- 用 Runnable 接口更容易与线程池等高级API配合,线程池只能放入实现 Runable 或 Callable 接口的线程,不能直接放入继承 Thread 的类。
- 用 Runnable 接口让任务类脱离 Thread 继承体系,使用更加灵活。
- Thread 和 Runnable 实际执行的内容是各自重写的run方法中的内容。
Runnable源码
@FunctionalInterface
public interface Runnable {
// Runnable是函数式接口,只有一个抽象run方法
public abstract void run();
}
Thread源码
// Thread实现了Runnable接口
public class Thread implements Runnable {
// Thread 与 Runnable 之间是 组合关系
// 合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
private Runnable target;
// 带有 Runnable 参数、线程名参数 的构造方法
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
private void init(...){
...
// 在init方法中将Runnable对象赋值给了 Thread 的成员变量 target
this.target = target;
}
// ...此处省略其余源码
// 1.传入的Runnable对象会在Thread的run方法中用到
@Override
public void run() {
// Runnable对象不为空,则调用Runnable对象重写的run方法
if (target != null) {
target.run();
}
}
}
3. 方法三,使用 FutureTask 配合 Thread
- FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况。
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建任务对象
FutureTask<Integer> futureTask = new FutureTask(() -> {
log.debug("callable执行任务");
Thread.sleep(1000);
return 100;
});
// 参数1 是任务对象; 参数2 是线程名
Thread t1 = new Thread(futureTask, "t1");
t1.start();
// 主线程阻塞,同步等待 futureTask 执行完毕的结果
Integer result = futureTask.get();
// 1秒后获取到结果
log.debug("获取FutureTask结果:{}", result);
}
22:36:52.884 c.FutureTaskThread [t1] - callable执行任务
22:36:53.899 c.FutureTaskThread [main] - 获取FutureTask结果:100
Process finished with exit code 0
Callable源码
// Callable是函数式接口,有返回结果,能抛出异常
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
FutureTask源码
// 1.FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况,get方法可以获取返回结果
// 2.FutureTask类是 Runnable接口 的扩展,FutureTask 既有Runnable中的run方法,又有Future中的get方法
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
}
// 接口可以多继承
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
4. 方法四,线程池创建线程
public static void main(String[] args) {
// 自定义线程池参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
10, // 非核心线程的存活时间
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
new AbortPolicy());
for (int i = 0; i < 20; i++) {
int finalI = i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " i:" + finalI);
});
}
}
- Runnable和Callable接口比较
- 相同点:
- 两者都是接口,都可创建线程,都需要调用Thread.start()启动线程。
- 不同点:
- 实现Callable接口的线程能返回执行结果;而实现Runnable接口的线程不能返回结果;
- Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的不允许抛异常;
- 实现Callable接口的线程可以调用Future.cancel取消执行,而实现Runnable接口的线程不能;