关于网上Java
多线程实现的方式
Oracle的官方文档
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of
Thread
. This subclass should override the run method of class Thread.
The other way to create a thread is to declare a class that implements the
Runnable
interface. That class then implements the run method.
- 继承
Thread
类
public class ThreadStyle extends Thread{
@Override
public void run() {
System.out.println("thread name is " + Thread.currentThread().getName() + ",thread id=" + Thread.currentThread().getId());
}
public static void main(String[] args) {
new ThreadStyle().start();
}
}
- 实现
Runnable
接口
public class RunnableStyle implements Runnable{
@Override
public void run() {
System.out.println("thread name is " + Thread.currentThread().getName() + ",thread id=" + Thread.currentThread().getId());
}
public static void main(String[] args) {
new Thread(new RunnableStyle()).start();
}
}
用Runnable
和Thread
的实现对比
结论:
Runnable
更好
- 从代码架构角度考虑:具体执行的任务(
run()
方法)应该和线程的创建、运行机制(Thread
类)是解耦的。 - 从资源的角度:继承
Thread
每次需要新建任务只能新建一个独立的线程,而新建一个独立的线程对资源的损耗是比较大的。如果使用Runnable
就可以使用线程池。 - 从继承的角度:继承
Thread
类Java只支持单继承,导致该类无法继承其它类,大大限制了可扩展性。
Runnable
和Thread
的本质
public class Thread implements Runnable {
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
Runnable
:最终调用target.run()
;Thread
:子类重新父类run()
;
同时使用Runnable
和Thread
会怎么样。
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("I am from Runnable.");
}
}){
@Override
public void run() {
System.out.println("I am from Thread.");
}
}.start();
}
输出:
I am from Thread.
从面向对象的思想去考虑: 内部类重写run
方法,相当于重写Thread
类的run
方法;创建Thread
的同时传入了Runnable
,根据Java语法规定,如果同时传入Runnable
,又重写了Thread
类的run
方法,由于Thread
的run
方法已经被重写,所以重写后的代码不再包含以下代码:
if (target != null) {
target.run();
}
也就是说传入的Runnable
不会再执行。
实现线程的方式总结
通常可以分为两类,来源Oracle官方。
准确来讲,创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元有两种方式。
- 方法一: 实现
Runnable
接口的run
方法,并把Runnable
实例传给Thread
类。 - 方法二: 重写
Thread
的run
方法(继承Thread
类)。
典型错误观点分析
- "
线程池
创建线程也算是一种新建现成的方式"
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
executorService.submit(new Task());
}
executorService.shutdown();
}
class Task implements Runnable{
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("current thread name is " + Thread.currentThread().getName());
}
}
线程池是由
DefaultThreadFactory
创建的,而创建线程是使用newThread
,查看源码
Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);
发现线程池还是使用
Thread
来创建的。
- “通过
Callable
和FutureTask
创建线程,也算是一种新建线程的方式”
Callable
结构图
Future
结构图
- 定时器
public static void main(String[] args) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println("current thread name is " + Thread.currentThread().getName());
}
},0, 1000);
}
查看
Timer
源码:
public Timer(String name) {
thread.setName(name);
thread.start();
}
private final TimerThread thread = new TimerThread(queue);
class TimerThread extends Thread {}
查看源码可知
TimerThread
继承于Thread
- 匿名内部类
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
System.out.println("current thread name is " + Thread.currentThread().getName());
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("current thread name is " + Thread.currentThread().getName());
}
}).start();
}
- Lambda表达式
new Thread(() -> System.out.println("current thread name is " + Thread.currentThread().getName())).start();
- 多线程的实现方式在代码中写法千变万化,但其本质
万变不离其宗。