進程是代碼在數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,線程則是進程的一個執行路徑,一個進程中至少有一個線程,進程中的多個線程「共享進程的資源」。java
操做系統在分配資源時是把資源分配給進程的,可是CPU資源比較特殊,它是被分配到線程的,由於真正要佔用CPU運行的是線程,因此也說「線程是CPU分配的基本單位」。web
以Java 爲例,咱們啓動一個main函數時,實際上就是啓動了一個「JVM 的進程」,「main函數」所在的線程就是這個進程的一個線程,也稱爲「主線程」。一個JVM進程中有多個線程,「多個線程共享進程的堆和方法區資源,可是每一個線程有本身的程序計數器和棧區域」。後端
內存是很是重要的系統資源,是硬盤和CPU的中間倉庫及橋樑,承載着操做系統和應用程序的實時運行。JVM 內存佈局規定了 Java 在運行過程當中內存申請、分配、管理的策略 ,保證了 JVM 的高效穩定運行。微信
若是按照線程是否共享來分類的話,以下圖所示:多線程
Thread類:異步
package java.lang;
public class Thread implements Runnable { // 構造方法 public Thread(Runnable target); public Thread(Runnable target, String name); public synchronized void start(); } 複製代碼
Runnable 接口編輯器
package java.lang;
@FunctionalInterface public interface Runnable { pubic abstract void run(); } 複製代碼
其實現繼承關係圖爲:Thread 實現了Runnbale接口,並重寫了Runide
其中target是一個Runnable對象,使用繼承的方式target爲null不會調用此方法函數
繼承的建立線程:佈局
public class ThreadDemo01 extends Thread {
@Override public void run() { super.run(); System.out.println("MyThread"); } public static void main(String[] args) { ThreadDemo01 thread1 = new ThreadDemo01(); thread1.start(); } } 複製代碼
咱們本身的MyThread線程類繼承Thread,重寫run方法
public class ThreadDemo2 {
public static void main(String[] args) { // 初始化線程實例 new Thread() { @Override public void run() { System.out.println(getName() + " is running"); } }.start(); } } 複製代碼
這種方式主要是Thread接口的構造函數的區分,當target不爲空的時候,會執行匿名內部類裏面的run方法
基於兩個參數的構造函數:
public class CreateThreadDemo3{
public static void main(String[] args) { RunnableThreadTest runnableThreadTest = new RunnableThreadTest(); new Thread(runnableThreadTest, "線程1").start(); new Thread(runnableThreadTest, "線程2").start(); } } /** * 實現Runnable接口的方式 */ class RunnableThreadTest implements Runnable{ private int i = 0; @Override public void run() { for (; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " is running: " + i); } } } 複製代碼
public class ThreadDemo2 {
public static void main(String[] args) { // 初始化線程實例 Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("匿名內部類..."); } }); thread.start(); } } 複製代碼
簡化java8的形式
public class ThreadDemo2 {
public static void main(String[] args) { // 初始化線程實例 Thread thread = new Thread(() -> System.out.println("匿名內部類...")); thread.start(); } } 複製代碼
和Runnable接口不同,Callable接口提供了一個call()方法做爲線程執行體,call()方法比run()方法功能要強大:call()方法能夠有返回值,能夠聲明拋出異常。
public class ThreadDemo5 {
public static void main(String[] args) { CallableTest callableTest = new CallableTest(); FutureTask futureTask = new FutureTask<>(callableTest); new Thread(futureTask).start(); try { System.out.println("子線程返回值:" + futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } class CallableTest implements Callable{ @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i < 100; i++) { sum += i; } System.out.println(Thread.currentThread().getName() + "running: " + sum); return sum; } } 複製代碼
Java5提供了Future接口來接收Callable接口中call()方法的返回值。 Callable接口是 Java5 新增的接口,不是Runnable接口的子接口,因此Callable對象不能直接做爲Thread對象的target。
針對這個問題,引入了RunnableFuture接口,RunnableFuture接口是Runnable接口和Future接口的子接口(下圖所示),能夠做爲Thread對象的target (實現RunnableFuture接口的子類能夠做爲Thread的target)。同時,Java5提供了一個RunnableFuture接口的實現類:FutureTask ,FutureTask能夠做爲Thread對象的target。
看一下FutrueTask的實現,能夠看出FutureTask即支持實現Callable接口的子類,也支持Runnable接口的子類,而且當傳過來的是Runnable接口的時候,會執行Executors的callable方法轉化爲callable接口的,執行的。
至於Future接口的原理,在後面的文章中再做分析,如今只是知道線程的建立方式。
整體來講通常的業務代碼中都是採用實現Runnable接口或者是Callable接口
❝想要獲取更多精選技術文章推薦的小夥伴們,請長按下圖,關注微信公衆號「先後端精選」,或者直接在微信上搜索「「先後端精選」」便可關注!天天推送最新的java技術精選知識
❞
本文使用 mdnice 排版