JDK8 创建的线程的两种方式
orcle文档解释
orcle文档
:
https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
总结:
准确的讲,创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元有两种方式:
-
方法一:实现
Runnable接口
的重写run方法,并把
Runnable
实例传给Thread类 -
方法二∶重写
Thread
的
run
方法(继承Thread类)
(其他一些方式,究其根本都是间接的通过上面两种方式创建的)
方式一:继承Thread类
public static void main(String[] args) {
System.out.println("主线程运行:" + Thread.currentThread().getName());
Task1 task = new Task1();
task.start();
}
static class Task1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("task1线程运行" + Thread.currentThread().getName());
System.out.println("Task11继承Thread实现多线程");
}
}
}
}
自定义线程类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
方式二:实现Runnable接口
public static void main(String[] args) {
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
}
static class Task implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("task1线程运行" + Thread.currentThread().getName());
System.out.println("Task11实现Runnable接口实现多线程");
}
}
}
定义MyRunnable类实现Runnable接口
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
同时用两种的情况
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("实现Runnable的run方法");
}
}){
@Override
public void run() {
System.out.println("匿名内部类,重写Thread类的run方法");
}
}.run();
}
结果:
匿名内部类,重写Thread类的run方法
分析:
这里使用的匿名内部类,在匿名内部类里面又重写了Thread类的run方法。创建Thread类的同时传入了Runnable对象,这时候Thread类的run已被重写,不再是原生方法的逻辑:执行传入task的run
所以即便传入Runnable,也不会被执行。
其他间接创建方式
Callable接口
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> ft = new FutureTask<>(task);
Thread thread = new Thread(ft);
thread.start();
}
static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
for (int i = 0; i < 20; i++) {
System.out.println("task1线程运行" + Thread.currentThread().getName());
System.out.println("Task11实现Callable接口实现多线程");
}
return 1;
}
}
FutureTask
实现了
RunnableFuture
接口,
RunnableFuture
继承了
Runnable
和
Future
接口。所以也是间接通过
Runnable
来创建的。
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行:Future result1 = ser.submit(t1);
- 获取结果: boolean r1 = result1.get()
- 关闭服务: ser.shutdownNow();
实现Runnable接口和Callable接口的区别
- Runnable 接口不会返回结果,Callable 接口可以返回结果
- Callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出
线程池
ThreadPoolExecutor
类的
execute
方法:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
//如果工作线程数小于核心线程数,
if (workerCountOf(c) < corePoolSize) {
//执行addWorker,会创建一个核心线程,如果创建失败,重新获取ctl
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果工作线程数大于等于核心线程数,线程池的状态是RUNNING,并且可以添加进队列
//(RUNNING状态下)如果添加失败,说明是队列已经满了,接着就去创建新的线程,如果大于最大线程数,则执行拒绝策略
//如果线程池不是RUNNING状态,则执行拒绝策略(当然还会调addWorker进行判断一次)
if (isRunning(c) && workQueue.offer(command)) {
//再次获取ctl,进行双重检索(也就是对线程池的状态再次检查一遍)
int recheck = ctl.get();
//如果线程池是不是处于RUNNING的状态,那么就会将任务从队列中移除,
//如果移除失败,则会判断工作线程是否为0 ,如果过为0 就创建一个非核心线程
//如果移除成功,就执行拒绝策略,因为线程池已经不可用了;
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
//线程池挂了或者大于最大线程数
reject(command);
}
addWork
方法会创建线程池内工作线程
Worker
。
Worker
这个线程,实现了
Runnable
接口,并持有一个线程thread,一个初始化的任务firstTask。thread是调用构造方法时通过
ThreadFactory
来创建的线程
ThreadFactory
中也是
Runnable
创建线程的模式。