[Q&A] 线程的创建方式?
Java创建多线程只有一种方式: Thread! 有且仅有 Thread
类能通过 start0()
方法 向操作系统申请线程资源。
在很多软文以及一些书籍中,经常会提到,创建线程有两种方式,第一种是构造一个Thread,第二种是实现Runnable接口,这种说法是错误的,最起码是不严谨的,在JDK中代表线程的就只有Thread这个类,线程的
执行单元
就是run方法,你可以通过继承Thread然后重写run方法实现自己的业务逻辑,也可以实现Runnable接口实现自己的业务逻辑。
-----------------------------------------------------------------------------读书笔记摘自书名:Java高并发编程详解:多线程与架构设计 作者:汪文君
通俗所说的线程创建方式
1. 继承Thread类
public class ExtendsRunningMan extends Thread {
private String threadTaskName;
public ExtendsRunningMan(String name) {
this.threadTaskName = name;
}
@Override
public void run() {
for (int i = 0; i <= 10; i++) {
System.out.println(Thread.currentThread().getId() + " " + Thread.currentThread().getName() + threadTaskName + i);
}
}
}
ExtendsRunningMan r1 = new ExtendsRunningMan("小明");
r3.start();
# 优点
符合大家的编程习惯
# 缺点
因为java类的单继承。如果当前类继承 Thread 类之后就不可以继承其他类。如果我们的类已经继承了一个类,则无法再继承 Thread 类。
应该怎么办呢?→ 引出第二种方式
2. 实现Runnable接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
public class RunnableRunningMan implements Runnable {
private String name;
public RunnableRunningMan(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getId() + " " + Thread.currentThread().getName() + " " + name + " " + i);
}
}
}
# 场景1:3个 Thread共享这个Runnable对象中的代码
RunnableRunningMan r1 = new RunnableRunningMan("小明");
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r1);
Thread t3 = new Thread(r1);
t1.start();
t2.start();
t3.start();
# 场景2:3个Thread独立执行各自Runnable对象中的代码
RunnableRunningMan r1 = new RunnableRunningMan("小明");
RunnableRunningMan r2 = new RunnableRunningMan("小张");
RunnableRunningMan r3 = new RunnableRunningMan("小李");
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
Thread t3 = new Thread(r3);
t1.start();
t2.start();
t3.start();
# 优点
1、可以避免由于Java的 单继承 特性而带来的局限
2、Runnable是可以 共享数据 的,适合多个线程处理同一资源的情况
# 个人理解是 :多线程的是避免浪费。多个Thread可以同时加载一个Runnable实例,是为了这个分的时间片的概率高,执行的更快点。
# 缺点
1、Runnable是可以共享数据的,可能会造成线程的不安全 (Runnable 造成线程的不安全)
2、Runnable是执行工作的独立任务,但是它不返回任何值
如果你希望任务在完成后能返回一个值,应该怎么办呢? → 引出第三种方式
3. 实现Callable接口
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
public class CallableRunningMan implements Callable<Integer> {
private String name;
public CallableRunningMan(String name) {
this.name = name;
}
@Override
public Integer call() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + name + " " + i);
}
return 100;
}
}
CallableRunningMan myThread1 = new CallableRunningMan("小明");
FutureTask<Integer> futureTask1 = new FutureTask<>(myThread1);
CallableRunningMan myThread2 = new CallableRunningMan("小明");
FutureTask<Integer> futureTask2 = new FutureTask<>(myThread2);
new Thread(futureTask1).start();
new Thread(futureTask2).start();
try {
System.out.println(Thread.currentThread().getName() + "返回值" + futureTask1.get());
System.out.println(Thread.currentThread().getName() + "返回值" + futureTask2.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
Runnable接口 和 Callable接口的区别
Runnable Callable
Since java1.1 Since java1.5
run() call()
无返回值 有返回值,FutureTask对象
不可抛异常 可以抛异常
加入线程池使用execute方法 加入线程池使用submit方法