一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java線程的start方法回調run方法的操作技巧

Java線程的start方法回調run方法的操作技巧

2021-02-02 11:53麥田 Java教程

面試過程中經常會被面試官問到為什么我們調用start()方法時會執行run()方法,為什么不能直接調用run()方法,問的一頭霧水,今天小編給大家介紹下Java線程的start方法回調run方法的操作技巧,需要的朋友參考下吧

面試中可能會被問到為什么我們調用start()方法時會執行run()方法,為什么我們不能直接調用run()方法?

Java 創建線程的方法

實際上,創建線程最重要的是提供線程函數(回調函數),該函數作為新創建線程的入口函數,實現自己想要的功能。Java 提供了兩種方法來創建一個線程:

繼承 Thread 類

?
1
2
3
4
5
class MyThread extends Thread{
 public void run() {
  System.out.println("My thread is started.");
 }
}

實現該繼承類的 run 方法,然后就可以創建這個子類的對象,調用 start 方法即可創建一個新的線程:

?
1
2
MyThread myThread = new MyThread();
myThread.start();

實現 Runnable 接口

?
1
2
3
4
5
class MyRunnable implements Runnable{
 public void run() {
  System.out.println("My runnable is invoked.");
 }
}

實現 Runnable 接口的類的對象可以作為一個參數傳遞到創建的 Thread 對象中,同樣調用 Thread#start 方法就可以在一個新的線程中運行 run 方法中的代碼了。

?
1
2
Thread myThread = new Thread( new MyRunnable());
myThread.start();

可以看到,不管是用哪種方法,實際上都是要實現一個 run 方法的。 該方法本質是上一個回調方法。由 start 方法新創建的線程會調用這個方法從而執行需要的代碼。 從后面可以看到,run 方法并不是真正的線程函數,只是被線程函數調用的一個 Java 方法而已,和其他的 Java 方法沒有什么本質的不同。

Java 線程的實現

從概念上來說,一個 Java 線程的創建根本上就對應了一個本地線程(native thread)的創建,兩者是一一對應的。 問題是,本地線程執行的應該是本地代碼,而 Java 線程提供的線程函數是 Java 方法,編譯出的是 Java 字節碼,所以可以想象的是, Java 線程其實提供了一個統一的線程函數,該線程函數通過 Java 虛擬機調用 Java 線程方法 , 這是通過 Java 本地方法調用來實現的。

以下是 Thread#start 方法的示例:

?
1
2
3
4
5
public synchronized void start() {
 
 start0();
 
}

可以看到它實際上調用了本地方法 start0, 該方法的聲明如下:

?
1
private native void start0();

Thread 類有個 registerNatives 本地方法,該方法主要的作用就是注冊一些本地方法供 Thread 類使用,如 start0(),stop0() 等等,可以說,所有操作本地線程的本地方法都是由它注冊的 . 這個方法放在一個 static 語句塊中,這就表明,當該類被加載到 JVM 中的時候,它就會被調用,進而注冊相應的本地方法。

?
1
2
3
4
private static native void registerNatives();
static{
 registerNatives();
}

本地方法 registerNatives 是定義在 Thread.c 文件中的。Thread.c 是個很小的文件,定義了各個操作系統平臺都要用到的關于線程的公用數據和操作,如代碼清單 1 所示。

清單1

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
JNIEXPORT void JNICALL
Java_Java_lang_Thread_registerNatives (JNIEnv *env, jclass cls){
 (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
static JNINativeMethod methods[] = {
 {"start0", "()V",(void *)&JVM_StartThread},
 {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
 {"isAlive","()Z",(void *)&JVM_IsThreadAlive},
 {"suspend0","()V",(void *)&JVM_SuspendThread},
 {"resume0","()V",(void *)&JVM_ResumeThread},
 {"setPriority0","(I)V",(void *)&JVM_SetThreadPriority},
 {"yield", "()V",(void *)&JVM_Yield},
 {"sleep","(J)V",(void *)&JVM_Sleep},
 {"currentThread","()" THD,(void *)&JVM_CurrentThread},
 {"countStackFrames","()I",(void *)&JVM_CountStackFrames},
 {"interrupt0","()V",(void *)&JVM_Interrupt},
 {"isInterrupted","(Z)Z",(void *)&JVM_IsInterrupted},
 {"holdsLock","(" OBJ ")Z",(void *)&JVM_HoldsLock},
 {"getThreads","()[" THD,(void *)&JVM_GetAllThreads},
 {"dumpThreads","([" THD ")[[" STE, (void *)&JVM_DumpThreads},
};

到此,可以容易的看出 Java 線程調用 start 的方法,實際上會調用到 JVM_StartThread 方法,那這個方法又是怎樣的邏輯呢。實際上,我們需要的是(或者說 Java 表現行為)該方法最終要調用 Java 線程的 run 方法,事實的確如此。 在 jvm.cpp 中,有如下代碼段:

?
1
2
3
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
 
 native_thread = new JavaThread(&thread_entry, sz);

**這里JVM_ENTRY是一個宏,用來定義**JVM_StartThread 函數,可以看到函數內創建了真正的平臺相關的本地線程,其線程函數是 thread_entry,如清單 2 所示。

清單2

?
1
2
3
4
5
6
7
8
9
static void thread_entry(JavaThread* thread, TRAPS) {
 HandleMark hm(THREAD);
 Handle obj(THREAD, thread->threadObj());
 JavaValue result(T_VOID);
 JavaCalls::call_virtual(&result,obj,
 KlassHandle(THREAD,SystemDictionary::Thread_klass()),
 vmSymbolHandles::run_method_name(),
vmSymbolHandles::void_method_signature(),THREAD);
}

可以看到調用了 vmSymbolHandles::run_method_name 方法,這是在 vmSymbols.hpp 用宏定義的:

?
1
2
3
4
5
class vmSymbolHandles: AllStatic {
 
 template(run_method_name,"run")
 
}

至于 run_method_name 是如何聲明定義的,因為涉及到很繁瑣的代碼細節,本文不做贅述。感興趣的讀者可以自行查看 JVM 的源代碼。

圖. Java 線程創建調用關系圖

Java線程的start方法回調run方法的操作技巧

start() 創建新進程
run() 沒有

PS:下面看下Java線程中run和start方法的區別

Thread類中run()和start()方法的區別如下:

run()方法:在本線程內調用該Runnable對象的run()方法,可以重復多次調用;

start()方法:啟動一個線程,調用該Runnable對象的run()方法,不能多次啟動一個線程;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.ljq.test;
public class ThreadTest {
  /**
   * 觀察直接調用run()和用start()啟動一個線程的差別
   *
   * @param args
   * @throws Exception
   */
  public static void main(String[] args){
    Thread thread=new ThreadDemo();
    //第一種
    //表明: run()和其他方法的調用沒任何不同,main方法按順序執行了它,并打印出最后一句
    //thread.run();
    //第二種
    //表明: start()方法重新創建了一個線程,在main方法執行結束后,由于start()方法創建的線程沒有運行結束,
    //因此主線程未能退出,直到線程thread也執行完畢.這里要注意,默認創建的線程是用戶線程(非守護線程)
    //thread.start();
    //第三種
    //1、為什么沒有打印出100句呢?因為我們將thread線程設置為了daemon(守護)線程,程序中只有守護線程存在的時候,是可以退出的,所以只打印了七句便退出了
    //2、當java虛擬機中有守護線程在運行的時候,java虛擬機會關閉。當所有常規線程運行完畢以后,
    //守護線程不管運行到哪里,虛擬機都會退出運行。所以你的守護線程最好不要寫一些會影響程序的業務邏輯。否則無法預料程序到底會出現什么問題
    //thread.setDaemon(true);
    //thread.start();
    //第四種
    //用戶線程可以被System.exit(0)強制kill掉,所以也只打印出七句
    thread.start();
    System.out.println("main thread is over");
    System.exit(1);
  }
  public static class ThreadDemo extends Thread{
    @Override
    public void run() {
      for (int i = 0; i < 100; i++) {
        System.out.println("This is a Thread test"+i);
      }
    }
  }
}

總結

以上所述是小編給大家介紹的Java線程的start方法回調run方法的操作技巧,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!

原文鏈接:http://blog.csdn.net/itmyhome1990/article/details/78471653

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本午夜色 | 女同志freelesvoices | 视频在线观看入口一二三2021 | 耽美调教高h | 欧美日韩中文字幕一区二区高清 | 国产欧美成人不卡视频 | 成人日b视频 | 九九大香尹人视频免费 | 午夜亚洲WWW湿好大 午夜想想爱 | 日本五十路六十30人8时间 | 久久无码AV亚洲精品色午夜麻豆 | 高肉h护士办公室play | 成人激情| 久久中文骚妇内射 | 欧美高清片 | 国产在线伊人 | 小早川怜子视频在线观看 | 亚洲国产成人久久综合一 | 女上男下gifxxoo动态视频 | 白鹿扒开内裤露出尿孔 | 亚洲天堂视频在线观看免费 | 精品国产在天天线在线麻豆 | 国内自拍网红在线自拍综合 | 香蕉免费一区二区三区在线观看 | 百合互慰吃奶互揉漫画 | 好大用力深一点女公交车 | 日本一区二区视频在线 | 福利片福利一区二区三区 | 国产区一二三四区2021 | 无遮挡激情 | 亚洲欧美日韩综合在线 | 国产一级一级一级成人毛片 | 精品久久久久久亚洲精品 | 免费二级毛片免费完整视频 | 97国产蝌蚪视频在线观看 | 日本在线看免费 | 亚洲国产日韩欧美在线vip1区 | 国内精品久久久久影院中国 | 深夜影院深a久久 | 国产专区亚洲欧美另类在线 | 久久亚洲精品AV无码四区 |