结论
话不多说,先上结果:线程直接调用run()方法就相当于一个普通对象调用的他的方法,而只有调用start()方法,线程才会启动,此时才具有抢占CPU资源的资格。当某个线程抢占到 CPU 资源后,会⾃动调⽤ run ⽅法。
分析
这篇博客不仅会讲解标题中的问题,还会说到关于线程的知识,所以我是重头分析的。
1、定义线程的常用的两种方式:
1、继承Thread 2、实现Runable接口。
(1)Thread的源码分析
可见Thread也是继承了Runable接口的,并且有一个run()方法。
target是什么呢?
就是一个任务。
(2)Runable接口源码
很简单,就是一个抽象的run方法。
小结:线程是去抢占 CPU 资源的,任务是具体执⾏业务逻辑的,线程内部会包含⼀个任务,线程启动(start),当抢占到资源之后,任务就开始执⾏(run)。
2、代码演示-自定义线程
1、继承Thread:
package com.demo.Thread;
/**
* @author wd
* @version 1.0
* @date 2020/3/12 23:08
*/
public class MyThread extends Thread {
@Override
public void run() {
for (int i=0; i<10; i++){
System.out.println("我是李四");
}
}
}
2、实现Runable
package com.demo.Thread;
/**
* @author wd
* @version 1.0
* @date 2020/3/12 23:46
*/
public class MyRunable implements Runnable {
@Override
public void run() {
for (int i=0; i<10; i++){
System.out.println("我是王五");
}
}
}
3、写个测试类:
(1)测试两种方式的区别
继承Thread的线程
public class threadTest {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
输出结果:
实现Runable接口
public class threadTest {
public static void main(String[] args) {
/*MyThread thread = new MyThread();
thread.start();*/
MyRunable task = new MyRunable();
Thread runThread = new Thread(task);
runThread.start();
}
}
输出结果:
小结:
- MyThread,继承 Thread 类的⽅式,直接在类中重写 run ⽅法,使⽤的时候,直接实例化MyThread,start 即可,因为 Thread 内部存在 Runnable。
- MyRunnbale,实现 Runnable 接⼝的⽅法,在实现类中重写 run ⽅法,使⽤的时候,需要先创建Thread 对象,并将 MyRunnable 注⼊到 Thread 中,Thread.start。
实际开发中推荐使⽤第⼆种⽅式。因为低耦合,在这里即把线程和任务分开了。
(2)Thread中run方法和start方法的区别
重写测试类:
public class threadTest {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.run();
for (int i =0; i<5; i++){
System.out.println("我是张三");
}
}
}
输出结果:
按照顺序执行的,因为thread1线程压根就没开启,所有操作都是主线程去做的。
把thread1.run();换成thread1.start();重写测试类:
public class threadTest {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.start();
for (int i =0; i<10; i++){
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是张三");
}
}
}
为了有效果,让主线程睡一睡。
输出结果:
可以看见此时,并不是代码顺序输出的。因为thread1线程开启,会和主线程抢占资源了。也就是有两个线程在跑了。
这也就是run和start两个方法的区别。
奇怪的指数增加了,线程的第三中实现方式
实现 Callable 接⼝
Callable 和 Runnable 的区别在于 Runnable 的 run ⽅法没有返回值,Callable 的 call ⽅法有返回值。
注意:Callable不可以和Runable一样直接用Trhead实现。实现方式如下图:
代码:
public class MyCallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc = new MyCallable();
FutureTask futureTask = new FutureTask(mc);
Thread thread1 = new Thread(futureTask);
Thread thread2 = new Thread(futureTask);
thread1.setName("A");
thread1.start();
thread2.setName("B");
thread2.start();
for (int i = 0;i < 5;i++){
System.out.println(i);
System.out.println(futureTask.get());
}
}
}
class MyCallable<String> implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("我是callable要执行的任务");
return (String) "我是返回的值";
}
}
从以上代码的输出结果可得到的一些结论:
- 两个Thread都应该输出“我是callable要执行的任务”的,可是只输出了一遍,因为Callable是有缓存的,所以任务只执行一遍。
- 代码中有三个线程,最先执行主线程,主线程中要拿futureTask的值,所以他停了,等待Callable线程返回值后再继续向下执行。