您好,登錄后才能下訂單哦!
這篇文章主要介紹“實現Java線程的方法有哪些”,在日常操作中,相信很多人在實現Java線程的方法有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”實現Java線程的方法有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
搞懂兩件事: 為什么說本質上只有一種實現線程的方式? 實現 Runnable 接口究竟比繼承 Thread 類實現線程好在哪里?
public class RunnableThread implements Runnable{ @Override public void run() { System.out.println("用實現Runnable接口實現線程"); } public static void main(String[] args) { Thread thread=new Thread(new RunnableThread()); thread.start(); } }
如代碼所示,首先通過 RunnableThread 類實現 Runnable 接口,然后重寫 run() 方法,之后只需要把這個實現了 run() 方法的實例傳到 Thread 類中就可以實現多線程。
public class ExtendsThread extends Thread{ @Override public void run() { System.out.println("用繼承Thread類實現線程"); } }
與第 1 種方式不同的是它沒有實現接口,而是繼承 Thread 類,并重寫了其中的 run() 方法。
下面是線程池中的源碼,來看看線程池是怎么實現線程的:
/** * The default thread factory */ static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
對于線程池而言,本質上是通過線程工廠創建線程的,默認采用 DefaultThreadFactory ,它會給線程池創建的線程設置一些默認值,比如:線程的名字、是否是守護線程,以及線程的優先級等。但是最終都是通過new Thread()創建線程的,只是參數多了些而已。
public class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { return new Random().nextInt(); } public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(10); Future<Integer> future = executorService.submit(new CallableTask()); System.out.println(future.get()); System.out.println(future.isDone()); System.out.println(future.isCancelled()); } }
Runnable 創建線程是無返回值的,而 Callable 和與之相關的 Future、FutureTask,它們可以把線程執行的結果作為返回值返回,如代碼所示,實現了 Callable 接口,并且給它的泛型設置成 Integer,然后它會返回一個隨機數。
但是,無論是 Callable 還是 FutureTask,它們首先和 Runnable 一樣,都是一個任務,是需要被執行的,而不是說它們本身就是線程。它們可以放到線程池中執行,如代碼所示, submit() 方法把任務放到線程池中,并由線程池創建線程,不管用什么方法,最終都是靠線程來執行的,而子線程的創建方式仍脫離不了最開始講的兩種基本方式,也就是實現 Runnable 接口和繼承 Thread 類。
public static void innerClassThread(){ new Thread(new Runnable() { @Override public void run() { System.out.println("匿名內部類創建線程"); } }).start(); } public static void lambdaThread(){ new Thread(()->{ System.out.println("lambda表達式創建線程"); }).start(); }
實際上,匿名內部類或 lambda 表達式創建線程,它們僅僅是在語法層面上實現了線程,并不能把它歸結于實現多線程的方式,如匿名內部類實現線程的代碼所示,它僅僅是用一個匿名內部類把需要傳入的 Runnable 給實例出來。
了解了上面的幾種創建線程的方式后,我們發現所有其他創建線程的方式都僅僅是在 new Thread() 外做了一層封裝而已;不同點僅僅在于實現線程運行內容的不同。
實現線程執行的內容,通過實現 Runnable 接口的方式
啟動線程需要調用 start() 方法,而 start() 方法最終還會調用 run() 方法,Thread中的run()方法源碼如下:
/* What will be run. */ private Runnable target; @Override public void run() { if (target != null) { target.run(); } }
target其實就是一個Runnable,實際線程執行的內容就是實現了Runnable的接口中的run方法。
實現線程執行的內容,繼承 Thread 類重寫 run() 方法的方式
啟動線程需要調用 start() 方法,而 start() 方法最終還會調用 run() 方法,但是此時的run()方法,因為繼承的原因,此時已被子類重寫了,所以此時線程執行的內容是子類的run方法。
運行內容主要來自于兩個地方,要么來自于 target,要么來自于子類重寫的 run() 方法;因此可以這樣總結:本質上,實現線程只有一種方式new Thread(),而要想實現線程執行的內容,卻有兩種方式,要么通過實現 Runnable 接口的方式,要么繼承 Thread 類重寫 run() 方法的方式,把我們想要執行的代碼傳入,讓線程去執行。
Runnable 里只有一個 run() 方法,它定義了需要執行的內容,在這種情況下,實現了 Runnable 與 Thread 類的解耦,Thread 類負責線程啟動和屬性設置等內容,職責分明。
java單繼承的原因,類一旦繼承了 Thread 類,那么它后續就沒有辦法再繼承其他的類,限制了代碼未來的可拓展性。
在某些情況下可以提高性能,如果使用繼承 Thread 類方式,每次執行一次任務,都需要新建一個獨立的線程;使用實現 Runnable 接口的方式,就可以把任務直接傳入線程池,使用一些固定的線程來完成任務,不需要每次新建銷毀線程,大大降低了性能開銷。
到此,關于“實現Java線程的方法有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。