Java 多線程入門,java多線程入門

來源:互聯網
上載者:User

Java 多線程入門,java多線程入門

進程與線程

在學習Java多線程之前,先簡單複習一下進程與線程的知識。

進程:進程是系統進行資源分派和調度的基本單位,可以將進程理解為一個正在執行的程式,比如一款遊戲。

線程:線程是程式執行的最小單位,一個進程可由一個或多個線程組成,在一款啟動並執行遊戲中通常會有介面

   更新線程、遊戲邏輯線程等,線程切換的開銷遠小於進程切換的開銷。

 

 

                   圖1

在圖1中,藍色框表示進程,黃色框表示線程。進程擁有代碼、資料等資源,這些資源是共用的,3個線程都可

以訪問,同時每個線程又擁有私人的棧空間。

Java線程狀態圖

 

 線程的五種狀態:

  1)建立狀態(New):線程對象執行個體化後就進入了建立狀態。

  2)就緒狀態(Runnable):線程對象執行個體化後,其他線程調用了該對象的start()方法,虛擬機器便會啟

     動該線程,處於就緒狀態的線程隨時可能被調度執行。

  3)運行狀態(Running):線程獲得了時間片,開始執行。只能從就緒狀態進入運行狀態。

  4)阻塞狀態(Blocked):線程因為某個原因暫停執行,並讓出CPU的使用權後便進入了阻塞狀態。

    等待阻塞:調用運行線程的wait()方法,虛擬機器會把該線程放入等待池。

    同步阻塞:運行線程擷取對象的同步鎖時,該鎖已被其他線程獲得,虛擬機器會把該線程放入鎖定池。

    其他線程:調用運行線程的sleep()方法或join()方法,或線程發出I/O請求時,進入阻塞狀態。

  5)結束狀態(Dead):線程正常執行完或異常退出時,進入了結束狀態。

Java線程實現

Java語言提供了兩種實現線程的方式:

1)通過繼承Thread類實現線程

public class ThreadTest {    public static void main(String[] args){        Thread thread = new MyThread(); //建立線程        thread.start();  //啟動線程    }}
//繼承Thread類class MyThread extends Thread{ @Override public void run() { int count = 7; while(count>0){ System.out.println(count); try { Thread.sleep(1000);   } catch (InterruptedException e) { e.printStackTrace(); } count--; } }}

2)通過實現Runnable介面實現線程

public class ThreadTest {    public static void main(String[] args){        Runnable runnable = new MyThread();
     //將Runnable對象傳遞給Thread構造器 Thread thread = new Thread(runnable); thread.start(); }}
//實現了Runnable介面class MyThread implements Runnable{ @Override public void run() { int count = 7; while(count>0){ System.out.println(count); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; } }}

兩種方式都覆寫了run()方法,run()方法內定義了線程的執行內容,我們只能通過線程的start()方法來

啟動線程,且start()方法只能調用一次,當線程進入執行狀態時,虛擬機器會回調線程的run()方法。直

接調用線程的run()方法,並不會啟動線程,只會像普通方法一樣去執行。

其實,Thread類本身也實現了Runnable介面。這兩種方式都可以實現線程,但Java語言只支援單繼

承,如果擴充了Thread類就無法再擴充其他類,遠沒有實現介面靈活。

線程常用方法

1)Thread類

  Thread():用於構造一個新的Thread。

  Thread(Runnable target):用於構造一個新的Thread,該線程使用了指定target的run方法。

  Thread(ThreadGroup group,Runnable target):用於在指定的線程組中構造一個新的Thread,該

  線程使用了指定target的run方法。

  currentThread():獲得當前運行線程的對象引用。

  interrupt():將當前線程置為中斷狀態。

  sleep(long millis):使當前啟動並執行線程進入睡眠狀態,睡眠時間至少為指定毫秒數。

  join():等待這個線程結束,即在一個線程中調用other.join(),將等待other線程結束後才繼續本線程。

  yield():當前執行的線程讓出CPU的使用權,從運行狀態進入就緒狀態,讓其他就緒線程執行。

2)Object類

  wait():讓當前線程進入等待阻塞狀態,直到其他線程調用了此對象的notify()或notifyAll()方法後,當

  前線程才被喚醒進入就緒狀態。

  notify():喚醒在此對象監控器上等待的單個線程。

  notifyAll():喚醒在此對象監控器上等待的所以線程。

       註:wait()、notify()、notifyAll()都依賴於同步鎖,而同步鎖是對象持有的,且每個對象只有一個,所以

    這些方法定義在Object類中,而不是Thread類中。

3)yield()、sleep()、wait()比較

   wait():讓線程從運行狀態進入等待阻塞狀態,並且會釋放它所持有的同步鎖。

   yield():讓線程從運行狀態進入就緒狀態,不會釋放它鎖持有的同步鎖。

   sleep():讓線程從運行狀態進入阻塞狀態,不會釋放它鎖持有的同步鎖。

線程同步

先看一個多線程類比賣票的例子,總票數7張,兩個線程同時賣票:

public class ThreadTest{    public static void main(String[] args){        Runnable r = new MyThread();        Thread t1 = new Thread(r);        Thread t2 = new Thread(r);        t1.start();        t2.start();    }}class MyThread implements Runnable{    private int tickets = 7;   //票數    @Override    public void run(){        while(tickets>0){            System.out.println("tickets:"+tickets);            tickets--;            try{                Thread.sleep(100);            }catch(InterruptedException e){                e.printStackTrace();            }        }    }}

 運行結果:

運行結果不符合我們的預期,因為兩個線程使用共用變數tickets,存在著由於交叉操作而破壞資料的可能性,

這種潛在的幹擾被稱作臨界區,通過同步對臨界區的訪問可以避免這種幹擾。

在Java語言中,每個對象都有與之關聯的同步鎖,並且可以通過使用synchronized方法或語句來擷取或釋放

這個鎖。在多線程協作時,如果涉及到對共用對象的訪問,在訪問對象之前,線程必須擷取到該對象的同步

鎖,擷取到同步鎖後可以阻止其他線程獲得這個鎖,直到持有鎖的線程釋放掉鎖為止。

public class ThreadTest{    public static void main(String[] args){        Runnable r = new MyThread();        Thread t1 = new Thread(r);        Thread t2 = new Thread(r);        t1.start();        t2.start();    }}class MyThread implements Runnable{    private int tickets = 7;    @Override    public void run(){        while(tickets>0){            synchronized(this){  //擷取當前對象的同步鎖                if(tickets>0){                    System.out.println("tickets:"+tickets);                    tickets--;                    try{                        Thread.sleep(100);                    }catch(InterruptedException e){                        e.printStackTrace();                    }                }            }        }    }}

運行結果:

synchronized用法:

1)synchronized方法:如果一個線程要在某個對象上調用synchronized方法,那麼它必須先擷取這個對象的

鎖,然後執行方法體,最後釋放這個對象上的鎖,而與此同時,在同一個對象上調用synchronized方法的其他

線程將阻塞,直到這個對象的鎖被釋放為止。

public synchronized void show(){    System.out.println("hello world");}

2)synchronized靜態方法:靜態方法也可以被聲明為synchronized的,每個類都有與之相關聯的Class對象,

而靜態同步方法擷取的就是它所屬類的Class對象上的鎖,兩個線程不能同時執行同一個類的靜態同步方法,如

果待用資料是線上程之間共用的,那麼對它的訪問就必須利用靜態同步方法來進行保護。

3)synchronized語句:靜態語句可以使我們擷取任何對象上的鎖而不僅僅是當前對象上的鎖,也能夠讓我們定

義比方法還要小的同步代碼區,這樣可以讓線程持有鎖的時間儘可能短,從而提高效能。

private final Object lock = new Object();public void show(){     synchronized(lock){         System.out.println("hello world");     }}

 

 

 

 

  

 

 

 

   

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.