首先,定义Thread类的子类并重写run()方法:html
package com.zwwhnly.springbootaction.javabase.thread; public class MyFirstThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.printf("[MyFirstThread]输出:%d,当前线程名称:%s\n", i, getName()); } } }
而后,建立该子类的实例并调用start()方法启动线程:java
package com.zwwhnly.springbootaction.javabase.thread; public class ThreadTest { public static void main(String[] args) { System.out.println("主线程开始执行,当前线程名称:" + Thread.currentThread().getName()); Thread firstThread = new MyFirstThread(); firstThread.start(); System.out.println("主线程执行结束,当前线程名称:" + Thread.currentThread().getName()); } }
运行结果以下所示:git
主线程开始执行,当前线程名称:maingithub
主线程执行结束,当前线程名称:main面试
[MyFirstThread]输出:0,当前线程名称:Thread-0spring
[MyFirstThread]输出:1,当前线程名称:Thread-0springboot
[MyFirstThread]输出:2,当前线程名称:Thread-0微信
[MyFirstThread]输出:3,当前线程名称:Thread-0多线程
[MyFirstThread]输出:4,当前线程名称:Thread-0并发
从运行结果能够看出如下2个问题:
firstThread.start();
,run()方法体中的代码并无当即执行,而是异步执行的。查看Thread类的源码,能够发现Thread类实现了接口Runnable:
public class Thread implements Runnable { // 省略其它代码 }
这里是重点,面试常问!
首先,定义Runnable接口的实现类并实现run()方法:
package com.zwwhnly.springbootaction.javabase.thread; public class MySecondThread implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.printf("[MySecondThread]输出:%d,当前线程名称:%s\n", i, Thread.currentThread().getName()); } } }
而后,调用Thread类的构造函数建立Thread实例并调用start()方法启动线程:
package com.zwwhnly.springbootaction.javabase.thread; public class ThreadTest { public static void main(String[] args) { Runnable target = new MySecondThread(); Thread secondThread = new Thread(target); secondThread.start(); } }
运行结果以下所示:
主线程开始执行,当前线程名称:main
主线程执行结束,当前线程名称:main
[MySecondThread]输出:0,当前线程名称:Thread-0
[MySecondThread]输出:1,当前线程名称:Thread-0
[MySecondThread]输出:2,当前线程名称:Thread-0
[MySecondThread]输出:3,当前线程名称:Thread-0
[MySecondThread]输出:4,当前线程名称:Thread-0
能够看出,使用这种方式和继承Thread类的运行结果是同样的。
首先,定义Callable接口的实现类并实现call()方法:
package com.zwwhnly.springbootaction.javabase.thread; import java.util.Random; import java.util.concurrent.Callable; public class MyThirdThread implements Callable<Integer> { @Override public Integer call() throws Exception { Thread.sleep(6 * 1000); return new Random().nextInt(); } }
而后,调用FutureTask类的构造函数建立FutureTask实例:
Callable<Integer> callable = new MyThirdThread(); FutureTask<Integer> futureTask = new FutureTask<>(callable);
最后,调用Thread类的构造函数建立Thread实例并调用start()方法启动线程:
package com.zwwhnly.springbootaction.javabase.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class ThreadTest { public static void main(String[] args) { System.out.println("主线程开始执行,当前线程名称:" + Thread.currentThread().getName()); Callable<Integer> callable = new MyThirdThread(); FutureTask<Integer> futureTask = new FutureTask<>(callable); new Thread(futureTask).start(); try { System.out.println("futureTask.isDone() return:" + futureTask.isDone()); System.out.println(futureTask.get()); System.out.println("futureTask.isDone() return:" + futureTask.isDone()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } System.out.println("主线程执行结束,当前线程名称:" + Thread.currentThread().getName()); } }
运行结果以下所示:
主线程开始执行,当前线程名称:main
futureTask.isDone() return:false
-1193053528
futureTask.isDone() return:true
主线程执行结束,当前线程名称:main
能够发现,使用Callable接口这种方式,咱们能够经过futureTask.get()
获取到线程的执行结果,而以前的2种方式,都是没有返回值的。
注意事项:调用
futureTask.get()
获取线程的执行结果时,主线程会阻塞直到获取到结果。
阻塞效果以下图所示:
如下是重点,面试常问!
关于第2点,能够经过以下示例来理解。
假如咱们总共有10张票(共享的资源),为了提高售票的效率,开了3个线程来售卖,代码以下所示:
package com.zwwhnly.springbootaction.javabase.thread; public class SaleTicketThread implements Runnable { private int quantity = 10; @Override public void run() { while (quantity > 0) { System.out.println(quantity-- + " is saled by " + Thread.currentThread().getName()); } } }
public static void main(String[] args) { Runnable runnable = new SaleTicketThread(); Thread saleTicketThread1 = new Thread(runnable); Thread saleTicketThread2 = new Thread(runnable); Thread saleTicketThread3 = new Thread(runnable); saleTicketThread1.start(); saleTicketThread2.start(); saleTicketThread3.start(); }
由于3个线程都是异步执行的,所以每次的运行结果多是不同,如下列举2次不一样的运行结果。
第1次运行结果:
10 is saled by Thread-0
8 is saled by Thread-0
7 is saled by Thread-0
5 is saled by Thread-0
9 is saled by Thread-1
3 is saled by Thread-1
2 is saled by Thread-1
1 is saled by Thread-1
4 is saled by Thread-0
6 is saled by Thread-2
第2次运行结果:
10 is saled by Thread-0
9 is saled by Thread-0
8 is saled by Thread-0
7 is saled by Thread-0
6 is saled by Thread-0
5 is saled by Thread-0
3 is saled by Thread-0
2 is saled by Thread-0
4 is saled by Thread-2
1 is saled by Thread-1
若是将上面的SaleTicketThread修改为继承Thread类的方式,就变成了3个线程各自拥有10张票,即变成了30张票,而不是3个线程共享10张票。
由于实现Runnable接口的优点,基本上实现多线程都使用的是该种方式,因此咱们将以前定义的MyFirstThread也修改成实现Runnable接口的方式:
package com.zwwhnly.springbootaction.javabase.thread; public class MyFirstThread implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.printf("[MyFirstThread]输出:%d,当前线程名称:%s\n", i, Thread.currentThread().getName()); } } }
而后仍然沿用以前定义的MyFirstThread、MySecondThread,咱们先看下调用start()的效果:
package com.zwwhnly.springbootaction.javabase.thread; public class ThreadTest { public static void main(String[] args) { System.out.println("主线程开始执行,当前线程名称:" + Thread.currentThread().getName()); Thread firstThread = new Thread(new MyFirstThread()); Runnable target = new MySecondThread(); Thread secondThread = new Thread(target); firstThread.start(); secondThread.start(); System.out.println("主线程执行结束,当前线程名称:" + Thread.currentThread().getName()); } }
运行结果(注意:屡次运行,结果可能不同):
主线程开始执行,当前线程名称:main
[MyFirstThread]输出:0,当前线程名称:Thread-0
[MyFirstThread]输出:1,当前线程名称:Thread-0
[MySecondThread]输出:0,当前线程名称:Thread-1
主线程执行结束,当前线程名称:main
[MySecondThread]输出:1,当前线程名称:Thread-1
[MySecondThread]输出:2,当前线程名称:Thread-1
[MySecondThread]输出:3,当前线程名称:Thread-1
[MySecondThread]输出:4,当前线程名称:Thread-1
[MyFirstThread]输出:2,当前线程名称:Thread-0
[MyFirstThread]输出:3,当前线程名称:Thread-0
[MyFirstThread]输出:4,当前线程名称:Thread-0
能够看出,调用start()方法后,程序中有3个线程,分别为主线程main、Thread-0、Thread-1,并且执行顺序不是按顺序执行的,存在不肯定性。
而后将start()方法修改成run()方法,以下所示:
firstThread.run(); secondThread.run();
此时的运行结果以下所示(屡次运行,结果是同样的):
主线程开始执行,当前线程名称:main
[MyFirstThread]输出:0,当前线程名称:main
[MyFirstThread]输出:1,当前线程名称:main
[MyFirstThread]输出:2,当前线程名称:main
[MyFirstThread]输出:3,当前线程名称:main
[MyFirstThread]输出:4,当前线程名称:main
[MySecondThread]输出:0,当前线程名称:main
[MySecondThread]输出:1,当前线程名称:main
[MySecondThread]输出:2,当前线程名称:main
[MySecondThread]输出:3,当前线程名称:main
[MySecondThread]输出:4,当前线程名称:main
主线程执行结束,当前线程名称:main
能够看出,调用run()方法后,程序中只有一个主线程,自定义的2个线程并无启动,并且执行顺序也是按顺序执行的。
如下是重点,面试常问!
在文章前面的章节中(1.2 实现Runnable接口 和1.3 实现Callable接口),咱们了解了如何使用Runnable、Callable接口来建立线程,如今咱们分别看下Runable和Callable接口的定义,其中,Runable接口的定义以下所示:
public interface Runnable { public abstract void run(); }
Callable接口的定义以下所示:
public interface Callable<V> { V call() throws Exception; }
由此能够看出,Runnable和Callable的区别主要有如下几点:
源码地址:https://github.com/zwwhnly/springboot-action.git,欢迎下载。
Java Thread 的 run() 与 start() 的区别
若是以为文章写的不错,欢迎关注个人微信公众号:「申城异乡人」,全部博客会同步更新。
若是有兴趣,也能够添加个人微信:zwwhnly_002,一块儿交流和探讨技术。