新手編程:Java多線程中Thread與Runnable的區別

2020-12-14 酷愛小島

Java多線程中Thread與Runnable的區別

定義

extends Thread

子類繼承Thread具備多線程能力,可以實現多線程;啟動線程的方法:①創建子類對象 ②對象名.start();不建議使用:避免多線程OOP單繼承的局限性(OOP:Object Oriented Programming,面向對象的編程、類似的還有OOD(面向對象的設計),OOA(面向對象的分析));implements Runnable

實現Runnable接口具有多線程能力,可以實現多線程啟動線程的方法:①創建子類對象 ②new Thread(對象).strart();推薦使用:避免OOP單繼承的局限性,方便被同一個對象多次使用;實際上所有的多線程代碼都是通過執行Thread的start()方法來運行的。所以不管是繼承Thread類還是實現Runnable接口來實現多線程,最終都是通過Thread對象的API來控制線程的,因此熟悉Thread類的API是進行多線程編程的基礎。(點擊查看什麼是API

示例

繼承Thread實現多線程1

package 多線程1;

public class TestThread1 extends Thread {

@Override

public void run() {

for (int i = 0; i < 20; i++) {

System.out.println("子線程正在執行..."+i);

}

}

public static void main(String[] args) {

TestThread1 testThread1 = new TestThread1();

testThread1.start();

for (int i = 0; i < 500; i++) {

System.out.println("主線程正在執行..."+i);

}

}

}

上述示例執行後可以看到:主線程和子線程的執行順序本該是「先執行子線程、再執行主線程」,事實上卻是主線程和子線程交替執行(無法人為控制,由CPU調度執行),這便是多線程的特徵;

繼承Thread實現多線程2——下載網絡圖片

package 多線程1;

import org.apache.commons.io.FileUtils;

import java.io.File;

import java.io.IOException;

import java.net.MalformedURLException;

import java.net.URL;

//練習網絡下載圖片

public class TestThread2 extends Thread {

private String url;

private String name;

public TestThread2(String url,String name){

this.name=name;

this.url=url;

}

@Override

public void run() {

WebDownloader webDownloader = new WebDownloader();

webDownloader.Downloader(url,name);

System.out.println("下載的文件名為 "+name);

}

public static void main(String[] args) {

TestThread2 t1 = new TestThread2("雙引號內填網絡圖片url","p1.jpg");

TestThread2 t2 = new TestThread2("雙引號內填網絡圖片url","p2.jpg");

TestThread2 t3 = new TestThread2("雙引號內填網絡圖片url","p3.jpg");

t1.start();

t2.start();

t3.start();

}

}

//下載器

class WebDownloader{

public void Downloader(String url,String name) {

try {

FileUtils.copyURLToFile(new URL(url),new File(name));

} catch (IOException e) {

e.printStackTrace();

System.out.println("IO錯誤!");

}

}

}

註:代碼中圖片為網絡上隨機尋找

由以上代碼也可以看出:下載的三張圖片的順序本應該是"p1-->p2-->p3",事實上執行後發現三張圖片的下載順序是隨機的(受CPU自由調度)。

Runnable接口實現多線程1

package 多線程1;

public class TestThread4_Runnable implements Runnable {

@Override

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println("我是小兔子"+i);

}

}

public static void main(String[] args) {

TestThread4_Runnable testThread4_runnable = new TestThread4_Runnable();

Thread thread = new Thread(testThread4_runnable);

thread.start();

for (int i = 0; i < 800; i++) {

System.out.println("但我是一隻小腦斧哦~"+i);

}

}

}

上述代碼的執行結果也是隨機的,無法根據代碼的編寫順序來判斷執行順序。

Runnable接口實現多線程2——龜兔賽跑

package 多線程1;

public class Race implements Runnable {

private String winner=null;

@Override

public void run() {

for (int i = 0; i <= 100; i++) {

if(Thread.currentThread().getName().equals("兔子") && i%5==0) { //模擬兔子休息(1ms)

try {

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

boolean winner = Win(i);

if(winner)

break;

System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");

}

}

public boolean Win(int steps){ //判斷比賽是否結束

if(winner!=null)

return true;

else if(steps==100) {

winner = Thread.currentThread().getName();

System.out.println(winner + "已經完成比賽!");

return true;

}

else

return false;

}

public static void main(String[] args) {

Race race = new Race();

new Thread(race,"兔子").start();

new Thread(race,"烏龜").start();

}

}

上述代碼模擬了龜兔賽跑,烏龜和兔子「隨機跑步」,兔子在中途「頻繁」休息,執行結果最終是烏龜成為了winner!

Thread.sleep()方法調用的目的是不讓當前線程獨自霸佔CPU,以留出一定時間給其他線程執行的機會。 實際上所有的多線程代碼執行順序都是不確定的,每次執行的結果都是隨機的。

但由於CPU的執行速度非常快,在測試的數量比較少的情況下可能無法看出隨機執行的現象!

相關焦點

  • Java多線程:帶你了解神秘的線程變量 ThreadLocal
    前言在 Java多線程中,線程變量ThreadLocal非常重要,但對於很多開發者來說,這並不容易理解,甚至覺得有點神秘今天,我將獻上一份 ThreadLocal的介紹 & 實戰攻略,讀取ThreadLocal變量中的值:get()// 返回一個Object對象String threadLocalValue = (String) myThreadLocal.get();
  • Java之Thread和Runnable接口的區別
    各位小夥伴們大家好,好久不見,在之前的文章中,小編介紹了創建線程的兩種方式Java之創建多線程的第一種方式,thread類。這次小編要講的是這兩種方式的區別。其實就是實現Runnable接口創建多線程的好處,具體如下:避免了單繼承的局限性,一個類只能繼承一個類,這個類繼承了Thread類就不能繼承其它類。但是,實現了Runnable接口,還可以繼承其它的類,實現其它接口。
  • Java Thread類簡述
    今天我們來看下java.lang.Thread這個類。一般來說,線程包括以下這幾個狀態:創建(new)、就緒(runnable)、運行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。 當需要新起一個線程來執行某個子任務時,就創建了一個線程。
  • 實現多線程的標準操作,基於Runnable接口實現java多線程
    1 為什麼要用Runnable上一篇文章介紹了通過繼承Thread類,實現java多線程。但如果當我們創建的這個線程類還想繼承其他類的時候,這種方法就略顯局限了。這也是java單繼承的局限性。為了避免這種局限性,所以又提供了第二種多線程主體定義的形式:實現Runnable接口。2 創建一個實現Runnable的對象我們先創建一個RunnableDemo類,並在裡面創建一個MyThread2內部類,MyThread2實現Runnable接口。
  • java入門避坑必讀,通過Thread類創建java多線程
    欲善編程,多看、敲、討論;動眼、手、大腦。1 為什麼要用多線程平常我們做crud的時候,用到多線程的機會不多。但當我們要處理一些複雜的業務時,或者提高程序處理效率時,就繞不開多線程的使用。也有些時候,我們需要對某個接口進行並發測試,也可以通過多線程來做一個性能測試小程序。2 創建一個java線程類我們先創建一個ThreadDemo類,並在裡面創建一個MyThread內部類,MyThread繼承Thread類。繼承之後,MyThread就是一個線程類了,具備了線程類的所有屬性。
  • 通俗易懂的告訴你「策略模式」在java多線程中的應用
    花10分鐘認真的閱讀一篇文章有時或許比敲60分鐘代碼還有效我們都知道java啟動多線程有兩種方式,一種是繼承Thread類,一種是實現Runnable接口,但是很多小夥伴可能不知道實現Runnable接口這種方式中運用了
  • 三萬字總結最全Java線程池源碼面試題
    1 為什麼要用線程池1.1 線程the more, the better?1、線程在java中是一個對象,更是作業系統的資源,線程創建、銷毀都需要時間。如果創建時間+銷毀時間>執行任務時間就很不合算。
  • Java之Thread類的常用方法之獲取線程名稱
    Java之多線程原理的內存圖解。獲取當前線程的名稱有兩種方法1.使用Thread類中的方法getName(),String getName()返回該線程的名稱。2.可以獲取當前正在執行的線程,使用線程中的方法getName()獲取線程的名稱。static Thread current Thread()返回對當前正在執行的線程對象的引用。
  • 「原創」Java並發編程系列33|深入理解線程池(上)
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫並發編程必不可少的線程池,接下來分兩篇文章介紹線程池,本文是第一篇。介紹1.1 使用場景並發編程可以高效利用CPU資源,提升任務執行效率,但是多線程及線程間的切換也伴隨著資源的消耗。當遇到單個任務處理時間比較短,但需要處理的任務數量很大時,線程會頻繁的創建銷毀,大量的時間和資源都會浪費在線程的創建和銷毀上,效率很低。
  • Java 實現線程的方式有幾種方式?帶有返回值的線程怎麼實現?
    Java 實現線程的方式有幾種方式?帶有返回值的線程怎麼實現?在Java線程開發中,有幾種方法開啟線程?假如需要得到線程返回的信息怎麼辦?可以實現嗎?凱哥將通過源碼和大家一起分享下線程怎麼將返回值帶回來的。
  • 「011期」JavaSE面試題(十一):多線程(1)
    ,主要總結了Java中的多線程問題,多線程分為三篇來講,這篇是第一篇,在後續,會沿著第一篇開篇的知識線路一直總結下去,做到日更!Q:線程和進程的區別?特點:線程的劃分尺度小於進程,這使多線程程序擁有高並發性,進程在運行時各自內存單元相互獨立,線程之間內存共享,這使多線程編程可以擁有更好的性能和用戶體驗
  • 項目實踐,使用Cyclic Barrier在多線程中設置屏障
    柵欄類似於閉鎖,它能阻塞一組線程直到某個事件的發生。柵欄與閉鎖的關鍵區別在於,所有的線程必須同時到達柵欄位置,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其他線程。Cyclic Barrer也可以用於解決多個線程之間的相互等待問題。
  • 給Java新手的一些建議——Java知識點歸納(Java基礎部分)
    寫這篇文章的目的是想總結一下自己這麼多年來使用java的一些心得體會,主要是和一些java基礎知識點相關的,所以也希望能分享給剛剛入門的Java程式設計師和打算入Java開發這個行當的準新手們,希望可以給大家一些經驗,能讓大家更好學習和使用Java。這次介紹的主要內容是和J2SE相關的部分,另外,會在以後再介紹些J2EE相關的、和Java中各個框架相關的內容。
  • Java並發編程:多線程如何實現阻塞與喚醒
    線程的阻塞和喚醒在多線程並發過程中是一個關鍵點,當線程數量達到很大的數量級時,並發可能帶來很多隱蔽的問題。如何正確暫停一個線程,暫停後又如何在一個要求的時間點恢復,這些都需要仔細考慮的細節。
  • 程式設計師:還不知道怎么正確地停止線程?
    前言線程在後臺開發中會經常用到,那你每次關閉線程都正確嗎?特別是目前多線程開發場景越來越多,給程式設計師造成的困擾就更多了。今天我們就來了了如何正確停止線程。如何正確地停止線程?一個線程一般都是正常的運行直到執行任務完畢而結束。那什麼時候我們需要對線程進行停止的動作呢?
  • Java是如何實現Future模式的?萬字詳解!
    在這一過程中,這一系列的單號都是我們收貨的重要憑證。因此,JDK的Future就類似於我們網購買東西的單號,當我們執行某一耗時的任務時,我們可以另起一個線程異步去執行這個耗時的任務,同時我們可以幹點其他事情。當事情幹完後我們再根據future這個"單號"去提取耗時任務的執行結果即可。因此Future也是多線程中的一種應用模式。
  • 記一次線上服務CPU 100%的處理過程 - 計算機java編程
    在容器內部執行 top 命令查看,定位到佔用CPU高的進程ID,使用 top -Hp <進程ID> 定位到佔用CPU高的線程ID。使用 jstack <進程ID> > jstack.txt 將進程的線程棧列印輸出。
  • 「原創」Java並發編程系列14|AQS源碼分析
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文為何適原創並發編程系列第 14 篇,文末有本系列文章匯總。;// 共享鎖acquireShared(int arg)// 共享式獲取同步狀態,與獨佔式的區別在於同一時刻有多個線程獲取同步狀態;acquireSharedInterruptibly(int arg)// 在acquireShared方法基礎上增加了能響應中斷的功能;
  • java網絡編程系列之NIO編程
    NIO編程一直是Java知識體系中的一個重點。前幾年的時間面試的門檻是了解NIO,現在就不一樣了,最起碼也要精通NIO,因此學習javaNIO編程是非常有必要的。這篇文章就開始對NIO進行一個認識。本文參考了慕課網,特在此說明。一、認識NIO1、什麼是BIO?
  • Java並發編程系列20|StampedLock源碼解析
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文為何適原創並發編程系列第 20 篇,文末有本系列文章匯總。這裡說明一點,本文的源碼分析重點在於鎖獲取與釋放過程中的狀態改變,線程入隊出隊以及等待操作不再做詳細介紹。