多線程的實現

繼承Thread類實現多線程

java.lang.Thread是一個線程操作的核心類。新建一個線程簡單的方法就是直接繼承Thread類,而後覆寫該類中的 run()方法(就相當於主類中的main方法)

package www.bit.java;

class MyThread extends Thread {
    private int ticket = 100;

    @Override
    public void run() {
        while(this.ticket > 0)
            System.out.println(Thread.currentThread().getName()+"還剩下"+this.ticket--+"票");
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        MyThread myThread1 = new MyThread();
        myThread.start();
        myThread1.start();
    }
}

爲什麼要通過start()方法來調用run()方法,而不是run()直接執行?

    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

  • 首先我們看到在start()方法中拋出IllegalThreadStateException異常,按照原有的處理方式,應當在調用處進行異常處理,而此處沒有處理也不會報錯,因此是一個RunTimeException,這個異常的產生只是因爲你重複啓動了線程纔會產生。所以,每一個線程對象只能夠啓動一次
  • 下面我們看到了在start()方法中調用了start0()方法,而這個方法是一個只聲明而未實現的方法同時使用native關鍵字進行定義。
  • native指的是調用本機的原生系統函數
  • Thread 類有個 registerNatives 本地方法,該方法主要的作用就是註冊一些本地方法供 Thread 類使用,如 start0(), stop0() 等等,可以說,所有操作本地線程的本地方法都是由它註冊的。
    這個方法放在一個 static 語句塊中,當該類被加載到 JVM 中的時候,它就會被調用,進而註冊相應的本地方法。
	private static native void registerNatives();
    static {
        registerNatives();
    }

本地方法 registerNatives 是定義在 Thread.c 文件中的。Thread.c 是個很小的文件,它定義了各個操作系統平臺都要用到的關於線程的公用數據和操作,

JNIEXPORT void JNICALL Java_Java_lang_Thread_registerNatives (JNIEnv \*env, jclass cls){ //registerNatives
            (\*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
static JNINativeMethod methods[] = {
        {"start0", "()V",(void \*)&JVM_StartThread}, //start0 方法
        {"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->start0 的方法,實際上會調用到 JVM_StartThread 方法,那這個方法又是怎麼處理的呢?
實際上,我們需要看到的是該方法終要調用 Java 線程的 run 方法,事實的確也是這樣的。
在 jvm.cpp 中,有如下代碼段:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)){    
	...    
	native_thread = new JavaThread(&thread_entry, sz);   
	... 
}

這裏JVM_ENTRY是一個宏,用來定義JVM_StartThread 函數,可以看到函數內創建了真正的平臺相關的本地線程, 其線程函數是 thread_entry,如下:

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(),    //LOOK! 看這裏    
    vmSymbolHandles::void_method_signature(),THREAD); 
}

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

class vmSymbolHandles: AllStatic {   
	...   
	template(run_method_name,"run")  //LOOK!!! 這裏決定了調用的方法名稱是 “run”!   
	... 
}

Runnable()接口實現多線程

Thread類的核心功能是進行線程的啓動。如果一個類爲了實現多線程直接去繼承Thread類就會有單繼承侷限。在 java中又提供有另外一種實現模式:Runnable接口

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

利用Runable接口實現線程主體類

  • 利用Runable接口實現線程主體類
class MyThread implements Runnable {
    private int ticket = 10;
    @Override
    public void run() {
        while (this.ticket > 0)
           System.out.println(Thread.currentThread().getName()+"還剩下"+this.ticket--+"票");
    }
}
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        new Thread(myThread,"黃牛1").start();
        new Thread(myThread,"黃牛2").start();
    }
}
  • 使用匿名內部類進行Runnable對象創建
public class TestThread {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            private int ticket = 100;

            @Override
            public void run() {
                while(this.ticket > 0)
                    System.out.println(Thread.currentThread().getName() + "還剩下" + this.ticket-- + "票");
            }
        };
        new Thread(runnable,"黃牛1").start();
        new Thread(runnable,"黃牛2").start();
    }
}
  • 使用Lamdba表達式進行Runnable對象創建
public class TestThread {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            int ticket = 10;
            while (ticket > 0){
                System.out.println(Thread.currentThread().getName() + "還剩下" + ticket-- + "票");
            }
        };
        new Thread(runnable,"黃牛1").start();
        new Thread(runnable,"黃牛2").start();
    }
}

繼承Thread類與實現Runnable接口的關係

  • Thread類與自定義線程類(實現了Runnable接口),是一個典型的代理設計模式。Thread類負責輔助真實業務操作(資源調度,創建線程並啓動)。自定義線程類負責真實業務的實現(run方法具體要做的事)。
  • 使用Runnable接口實現的多線程程序類可以更好的描述貢獻的概念。

Callable實現多線程

從JDK1.5開始追加了新的開發包:java.uti.concurrent。這個開發包主要是進行高併發編程使用的,包含很多在高併發操作中會使用的類。在這個包裏定義有一個新的接口Callable

@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;
}

Runnable中的run()方法沒有返回值,它的設計也遵循了主方法的設計原則:線程開始了就別回頭。但是很多時候需要一些返回值,例如某些線程執行完成後可能帶來一些返回結果,這種情況下就只能利用Callable來實現多線程。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String>{
    private int ticket = 1000;
    @Override
    public String call() {
        while (this.ticket > 0){
            System.out.println(Thread.currentThread().getName() + "還剩下" + ticket-- + "票");
        }
        return "票賣完了!!!";
    }
}
public class TestThread {
    public static void main(String[] args) throws ExecutionException,InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new MyThread());
        new Thread(futureTask,"黃牛1").start();
        new Thread(futureTask,"黃牛2").start();
        System.out.println(futureTask.get());
    }
}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章