https://zhuanlan.zhihu.com/p/107479862java
JAVA 最難學的部分是哪裏?不少朋友都會說:「 java 多線程 」。程序員
隨着業務量和數據的增長,企業不可避免地會使用多線程的方式處理數據。在 Java 職位的面試中,多線程也是必考的高階知識點之一。能夠說,java多線程是衡量一名 Java 程序員是否資深的關鍵標準之一。web
今天,咱們就來學習一下 Java 多線程的概念吧!面試
(點擊課程連接,開啓實驗環境,邊學邊練纔是更有效的學習方式)數據庫
Java 多線程技術實戰 www.shiyanlou.com初步建立多線程,理清多線程的概念。多線程
進程是程序在計算機上的一次執行活動。當你運行一個程序,你就啓動了一個進程。凡是用於完成操做系統的各類功能的進程就是系統進程,而全部由你啓動的進程都是用戶進程。
進程是程序在計算機上的一次執行活動。當你運行一個程序,你就啓動了一個進程。凡是用於完成操做系統的各類功能的進程就是系統進程,而全部由你啓動的進程都是用戶進程。異步
如圖所示每個正在運行的 .exe
程序都是一個進程。ide
進程就是有一個或多個線程構成的。而線程是進程中的實際運行單位,是獨立運行於進程之中的子任務。是操做系統進行運算調度的最小單位。可理解爲線程是進程中的一個最小運行單元。函數
一個進程下包含 N 個線程。學習
舉例說明:玩英雄聯盟的時候,打開客戶端便啓動了許多個線程:排隊隊列線程、好友聊天線程、正在支付線程。在英雄聯盟這一個進程之下便啓動了 N 個線程。
咱們初學 java 邊寫代碼的時候,一般使用 main 方法進行運行,此時 main 方法執行的即是一個主線程,而所謂的多線程,便是在主線程執行的過程當中,同時執行其餘的線程。可是同時執行多個線程容易出現報錯現象,例如同時同分同秒,兩個線程同時修改一個 txt、數據庫表文件,或第一個線程沒有修改完 txt、數據庫表文件,第二個線程同時也去修改。這即是線程之間的混亂、資源競爭、髒讀,即是程序員須要去解決的疑難雜症。
java 世界中有兩種方式建立多線程,分別是繼承 Thread 類,實現 Runnable 接口。
第一步:在 webide 上右鍵單擊菜單,選擇 New File 建立新文件。
第二步:建立文件名爲 test0.java
第三步:編寫 test0.java
中繼承 Thread 類方式建立多線程的代碼以下所示:
public class test0 {
public static void main(String[] args) {
Thread MyThread = new MyThread();
MyThread.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("hello myThread" + Thread.currentThread().getName());
}
}
第四步:編譯 test0.java
代碼:
javac test0.java
編譯以後,會產生咱們所編寫的 test0 類與 MyThread 類
第五步:運行 test 代碼:java test0
只須要把《建立多線程 —— 繼承 Thread》中代碼修改爲以下所示便可,其它操做不變:
public class test0 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run(){
System.out.println("hello myRunnable" + Thread.currentThread().getName());
}
}
執行結果以下所示:
一般狀況下,若是建立的線程類已經含有父類時候,此時因爲 Java 語法結構不支持多繼承的緣由,不可以再次繼承 Thread 類,此時則須要使用實現 Runnable 接口的方式來應對如此場景。另外值得說明的是,Thread 類也實現了 Runnable 接口。
因爲多線程是由繼承 Thread
或實現 Runnable
並重寫 run()
方法,經過 thread.start()
進行運行的,而自己重寫的 run()
方法是不具有傳參能力的,那我新建的線程就接受不到我所想傳入的參數了麼?
study1.java
文件class ThreadA extends Thread{
private String age;
public ThreadA(String age){
this.age = age;
}
@Override
public void run() {
System.out.println("age=" + age);
}
}
public class study1 {
public static void main(String[] args) {
String age = new String("12");
ThreadA a = new ThreadA(age);
a.start();
}
}
不管 extendsThread
仍是 implementsRunnable
,傳參都須要使用線程初始化的有參構造形式,達到多線程傳參的目的。也能夠作到重載有參構造,傳入各式對象。
Callable<V>
一般意義上理解確實 Java 實現多線程的方式有繼承 Thread
和實現 Runnable
,可是若是想實現多線程而且具備返回值的狀況下,須要實現 Callable<V>
接口,這個接口是 JDK1.5
版本之後纔出現的接口。
study2.java
建立 study2.java
文件,利用實現 Callable<V>
進行返回,代碼以下所示:
import java.util.concurrent.Callable;
public class study2 {
public static void main(String[] args) {
MyCallable MyCallable = new MyCallable("張方興");
String call = null;
try {
call = MyCallable.call();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(call);
}
}
class MyCallable implements Callable<String>{
private String name;
public MyCallable(String name) {
this.name = name;
}
@Override
public String call() throws Exception {
return "call:" + name;
}
}
Callable<V>
接口詳解通常繼承 Thread
的類,含有 .start()
函數,因此直接可使用 .start()
函數進行啓動。實現 Runnable
的類,須要經過 newThread(myRunnable).start();
的方式進行啓動,即實現 Runnable
的類只是作好了一段多線程所需執行的內容,自身並無執行的能力,須要經過 Thread
類的 .start()
函數進行啓動。實現 Callable<V>
的接口,含有 .call()
函數,因此能夠直接使用 .call()
函數進行啓動,另外值得說明的是, Callable<V>
函數具備返回值,返回值爲定義類時使用的 <V>
類型,其定義是其返回。 Callable<V>
接口定義以下所示:
@FunctionalInterface
public interface Callable<V> {
/** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */
V call() throws Exception;
}
Callable<V>
用於指示接口類型聲明是由 Java 語言規範定義的功能接口。從概念上講,函數接口只有一個抽象方法。由於 java.lang.reflect.Method#isDefault()default methods
有一個實現,因此它們不是抽象的。若是接口聲明一個抽象方法重寫 java.lang.Object
的一個公共方法,則該方法也不計入接口的抽象方法計數,由於接口的任何實現都將具備來自 java.lang.Object
或其餘位置的實現。另外注意,函數接口的實例可使用 lambda 表達式、方法引用或構造函數引用建立。
Callable<V>
在須要使用返回值的狀況下,程序是同步運行的Callable<V>
。其它狀況下,程序是異步運行的
完整課程內容,請在實驗樓邊操做邊學習。
Java 多線程技術實戰 www.shiyanlou.com