java線程概述

來源:互聯網
上載者:User

標籤:


/**
多線程概述:什麼是多線程?
首先,什麼是進程?
進程是系統分配資源的基本單位,對於windows系統而言。
進程是程式的一次運行。
一個進程中至少有一個線程存在,因為線程才是實際運行單元。
線程:是指程式中的控制單元,一條執行路徑。
一個程式可以有多線程並發執行,每個線程是一個控制單元。一個程式可以有多條執行線路,
每個執行線路代表一個線程。
線程的建立是由底層系統完成的,對於Java中的線程是jvm通過調用底層的windows作業系統的功能建立的。

注意一個線程在程式上的體現和記憶體中體現就是一個對象,一個線程對象。用一個Thread類或者其子類可以建立
很多個不同的線程對象,即很多個不同的線程。

線程的建立:線程的建立方式有兩種:
1、通過繼承Thread類,覆寫其中的run方法。
2、通過實現Runnable介面,覆寫其中的run方法。

線程是一類事物,是指程式的一個控制單元。Java中有對於此類的描述,叫做Thread類,該類中的run方法,即
public void run(){};,這個空參數的內部什麼操作也沒有的方法是供Thread的子類覆寫其,並在run中存放
該線程要執行的代碼。
Thread類的一些方法:
public void run();
public String getName();//返回該線程的名稱。
public String setName(String name);//設定該線程的名稱。
public static Thread currentThread();//返回對當前正在執行的線程對象的引用。
public static void sleep(long millis,int nanos);//使得執行該方法的線程進入到阻塞狀態,開始休眠。
第一種建立方式:
1、通過繼承Thread類。
2、覆寫其中的run方法。
3、將該線程需要啟動並執行代碼存放到run()方法中
4、建立子類對象
5、調用子類對象的start()方法,開啟線程,並調用run方法。

第二種建立方式:
1、通過實現Runnable介面,定義類。
2、覆寫其中的run方法。
3、將需要線程啟動並執行代碼存放到run()方法中
4、建立自訂類的對象。
5、建立Thread類的對象,並將自訂類的對象作為參數傳遞給Thread類的帶參數的建構函式。建立出一個Thread類的
對象。
6、調用Thread類對象的start()方法,開啟線程,並調用自訂類對象的run方法。

兩種方式的區別比較:
區別:
第二種方式,實現Runnable介面的類並不是一個線程類,建立該介面的子類對象,並不是建立了線程對象。
第二種方式是通過建立Thread類的對象,並指定所要啟動並執行run方法位於哪個對象中,來建立線程對象的。

優缺點:第一種方式,由於Java的單繼承,使得Thread子類不能在繼承其他的類,無法成為其他類的一種。有一定的
局限性。而第二種方式,通過實現Runnable介面的方式,避免了繼承Thread的,所以是避免了單繼承的局限性。
在實際開發中儘可能使用第二種方式,第二種方式是將Runnable介面所定義的功能擴充到了介面子類上。
凡是實現了Runnable介面的類的對象,都可以交給Thread類的對象來運行其中特定的內容。

線程的運行狀態:
一個線程的運行狀態有5種:是一個類似於生物一樣的聲明周期。
分別是建立狀態、就緒狀態、運行狀態、阻塞狀態、死亡狀態。具體如下:

1、建立(new): 用new語句建立完一個線程,該線程處於建立狀態,此時它和其他java對象一樣,僅僅在Heap中被分配了記憶體。
當一個線程處於建立狀態時,它僅僅是一個空的線程對象,系統不為它分配資源。
例如:Thread t = new Thread(new Runner());
2、就緒(runnable): 程式通過線程對象調用啟動方法start()後,系統會為這個線程分配它運行時所需的除處理器cpu之外的
所有系統資源。這時,它處在隨時可以啟動並執行狀態,在隨後的任意時刻,只要它獲得處理器即會進入運行狀態(running)。
例如:t.start();
3、運行(running): 處於這個狀態的線程佔用cpu,執行程式代碼。在並發環境中,如果電腦只有一個cpu,那麼任何時刻
只會有一個線程處於這個狀態。如果電腦中有多個cpu(即多核),那麼同一時刻可以讓幾個線程佔用不同的cpu,使它們都處
於運行狀態,只有處於就緒狀態(runnable)的線程才有機會轉到運行狀態。其他線程狀態不可以直接切換到運行狀態,
必須先進入就緒狀態才可以。
4、阻塞(blocked): 阻塞狀態是指線程因為某些原因放棄cpu,暫時停止運行。當線程處於阻塞狀態時,java虛擬機器不會給
線程分配cpu,直到線程重新進入就緒狀態,它才有機會轉到運行狀態。
阻塞狀態可以分為以下3中:
@1,位於對象等待池中的阻塞狀態(blocked in object‘s wait pool):當線程處於運行狀態時,如果
執行了某個對象的wait()方法,java虛擬機器就會把線程放到這個對象的等待池中。
@2,位於對象鎖池中的阻塞狀態(blocked in object‘s lock pool):當線程處於運行狀態,
試圖獲得某個對象的同步鎖時,如果該對象的同步鎖已經被其他線程佔用,JVM就會把這個線程放到這個
對象的鎖池中。
@3,其他阻塞狀態(otherwise blocked):當前線程執行了sleep()方法,或者調用了其他線程的join()方法,
或者發出了I/O請求時,就會進入這個狀態。當一個線程執行System.out.println()或者System.in.read()方法時,
就會發出一個I/O請求,該線程放棄cpu,進入阻塞狀態,直到I/O處理完畢,該線程才會恢複執行。
5、死亡(dead): 當線程退出run()方法時,就進入死亡狀態,該線程結束生命週期。線程有可能是正常執行完run()方法而
退出,也有可能是遇到異常而退出。不管線程是正常結束還是異常結束,都不會對其他線程造成影響。

線程狀態之間的切換,建立狀態只可以進入就緒,就緒可以到運行,運行也可以到就緒,運行可以到阻塞,阻塞可以到
就緒,建立、就緒、運行、阻塞都可以到死亡。

多線程的安全問題:當多個線程執行同一塊代碼,操作共用資料的時候,就會出現安全執行緒問題。

安全執行緒問題出現的前提:
1、至少有兩個或者兩個以上的線程存在。
2、至少有兩條或者兩條以上的代碼操作共用資源。

解決方案:讓線程同步。
什麼事線程同步?什麼是線程非同步?
線程具有隨機性或者說非同步性,即每個線程都以各自獨立的、不可預知的速度向前推進。即每個線程各自執行各自
的,互不相干。
同步是指:因為需要互斥的使用某一個共用資源,或者需要協同合作而產生了相互制約關係。即線程的推進執行中
需要考慮其他線程的執行情況了。
解決安全執行緒的問題,是使用同步代碼塊或者同步函數。關鍵字為synchronized。

同步代碼塊:
synchronized(對象)
{
需要同步的代碼塊
}
同步函數:
synchronized 傳回值類型 函數名()
{
//函數體,需要同步的代碼。
}
同步的部分一次只能有一個線程進入運行,同步的部分都有一個鎖,無論是同步代碼還是同步函數,這個鎖是一個對象。
對於同步代碼塊就是指synchronize(對象)中的對象。而對於同步函數,如果是非靜態同步函數,則是調用該
非靜態同步函數的對象,即this;如果是靜態同步代碼塊,則是該靜態同步函數所屬的類的類對象,即該類的
位元組碼檔案對象,即類名.class,“類名.class”就是這個對象的名稱,該對象的類型是Class類型。
同步部分的進入規則是,只有拿到鎖的線程才可以進入同步部分執行,執行完同步部分後,再將該鎖釋放,沒有拿到該
鎖的線程無法進入同步部分,只能是在外等候,因為缺少資源,放棄CPU,進入阻塞狀態。

同步:
好處:解決了安全執行緒問題
弊端:因為每次進入同步部分,都需要對同步部分進行鎖判定,降低了程式的效率。但有時這種犧牲是必要的。

死結:死結是指由於多個線程之間佔有對方需要的鎖,而又請求對方佔有的鎖,造成的一個無法解開的局面,死結。
死結產生的原因:同步中嵌套同步。
例1:
synchronized(lockA)
{
synchronzied(lockB)
}
synchronized(lockB)
{
synchronized(lockA)
}
例2:
sychronized void show()
{
method();
}
sychronized void method()
{
show();
}
單例模式中的懶漢式,即消極式載入的方式,當有多線程訪問時,會出現安全執行緒問題。解決方案是加同步代碼塊或者同步函數。
但是加入同步後,會降低程式的效率,因為每個線程在進入同步部分時,都要進行同步鎖的判斷。而最佳化的方案是,使用
雙重判斷,來提高程式的效率。
*/

class Demo extends Thread
{
Demo(String name)
{
super(name);
}
public void run()
{
for(int x=0; x<60; x++)
{
System.out.println(Thread.currentThread().getName()+"---run----"+x);
}
}
}

class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo("Demo");
//System.out.println(d.getName());

//d.run();

d.start();
for(int x =0 ;x<60; x++)
{
System.out.println(Thread.currentThread().getName()+"---main---"+x);
}

}
}
/*
主函數也是一個線程,它的名稱叫做main,一般稱為主線程。
而其他線程的名稱可以在建立時就賦值,如果使用預設名稱,則為Thread-編號。
run()和start()的區別:
run方法只是用來儲存線程需要啟動並執行代碼。
start()方法是用來開啟線程的,只有開啟線程,系統才會給該線程分配除cpu以外的資源。然後會調用run方法。
*/

java線程概述

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.