導航:首頁 > 編程語言 > java8線程

java8線程

發布時間:2023-05-01 22:16:35

java 8 新特性中p->p.setLastName("Doe"));

這是lambda表達式,也就是匿名函數,對於
persons.forEach(p->p.setLastName("Doe"));
這句代碼,persons是一個list,forEach是一個內部迭代的方法,p->p.setLastName("Doe")是一個匿名函數對象。
這個語句等價於
for (Person p : persons) {
p.setLastName("Doe");
}
但是用for循環是外部迭代,速度略慢。
p->p.setLastName("Doe")
p是指list當中取出的對象,p可以自己定義,你寫a也可以,相應的就要變成
a->a.setLastName("Doe")
->是lambda表達式的符號,->左邊的對象去傳入lambda函數體枯清p.setLastName("Doe"),是前面傳入的p去調用setLastName方法,如果要調用多個方法,那麼方法體要用花括弧包含
例如persons.forEach(p->{
p.setLastName("Doe")
System.out.println(p);
});
綜上所述
persons.forEach(p->p.setLastName("Doe"));
是把p->p.setLastName("Doe");這個函數對象,給了persons的迭代器,讓迭代器用這個函數去對集合中的每一個對象使用
有了lambda,你可以這樣定義一個新的線程
new Thread(() -> {System.out.println("hello word!");}).start();
() -> {System.out.println("hello word!");}這句語句其實就是runnable介面的實現對象,
編譯器會根據函數需要實現的介面去隱式轉換,Thread的構造方法需要傳遞Runnable介面的實現類,所以new Thread(() -> {System.out.println("hello word!");}).start();相當於
new Thread((Runnable)() -> {System.out.println("hello word!");}).start();
其中()是匹配接鎮逗口中的無參函數,因為Runnable介面中run方法為無參所以是()
->指明前面是傳入參數,後面是函數體
有了lambda就能寫出更加漂亮的代碼,以及更少的工作量,實現一個方法的介面用匿名內部類的話看起沒旅前來是冗餘代碼了
純手打望採納

Ⅱ 《Java線程與並發編程實踐》pdf下載在線閱讀全文,求百度網盤雲資源

《Java線程與並發編程實踐》網路網盤pdf最新全集下載:
鏈接: https://pan..com/s/1zebgAWKpIEWptv9zB_Y2GA

?pwd=amsm 提取碼: amsm
簡介:《Java線程與並發編程實踐》是針對Java 8中的線程特性和並發工具的快速學習和實踐指南。Java線程和並發工具是應用開發中的重要部分,備受開發者的重視,也有一定的學習難度。適合有一定基礎的Java程序員閱讀學習.


Ⅲ JAVA線程的機制有哪些

Java的線程機制 摘 要: 多線程機制是Java的重要技術,闡述了線程和進程的差別;Java中線程4個狀態之間的轉換;並結合例子說明了兩種創建線程的方法。 線程是指程序中能順序執行的一個序列。一個線程只有一個入口點� 但可能有幾個出口點� 不過,每個時刻的執行點總是只有一個。線程是不能獨立運行的程序,而只是某個整體程序內部的一個順序執行流。 多線程是Java的一個重要特點。如果一個程序是單線程的,那麼,任何時刻都只有一個執行點。這種單線程執行方法使系統運行效率低,而且,由於必須依靠中斷來處理輸入/輸出。所以,當出現頻繁輸入/輸出或者有優先順序較低的中斷請求時,實時性就變得很差。多線程系統可以避免這個缺點。所謂多線程,就是通過系統的調度使幾個具有不同功能的程序流即線程同時並行地運行。 在單處理器計算機系統中,實際上是不可能使多個線程真正並行運行的,而要通過系統用極短的時間、極快的速度對多個線程進行切換,宏觀上形成多個線程並發執行的效果。 1 線程和進程機制上的差別 線程和進程很相象,它們都是程序的一個順序執行序列,但兩者又有區別。進程是一個實體,每個進程有自己獨立的狀態,並有自己的專用數據段,創建進程時, 必須建立和復制其專用數據段,線程則互相共享數據段。同一個程序中的所有線程只有一個數據段, 所以, 創建線程時不必重新建立和復制數據段。由於數據段建立和復制這方面的差異,使線程的建立和線程間的切換速度大大優於進程,另一方面,線程又具備進程的大多數優點。 假設銀行系統辦理存款和取款手續,將帳本看成數據段。如果按進程這種機制,那麼,當儲戶去存/取款時,銀行應先把帳本復制一遍,為儲戶建立一個獨立的帳本再結算。如果按線程機制, 那麼,銀行里所有的出納員都用同一個帳本,儲戶來辦存/取款時,也從這個帳本直接結算。用線程機制省去了數據段復制這一步顯然是線程獨具的特點。 由於多個線程共享一個數據段,所以,也出現了數據訪問過程的互斥和同步問題,這使系統管理功能變得相對復雜。 總的來說,一個多線程系統在提高系統的輸入/輸出速度、有效利用系統資源、改善計算機通信功能以及發揮多處理器硬體功能方面顯示了很大優勢。因此,一些最新的操作系統如Windows95、Windows98、Windows NT等都提供了對多線程的支持。但是,在多線程操作系統下設計多線程的程序仍然是一個比較復雜和困難的工作。由於需要解決對數據段的共享,所以,原則上應該從程序設計角度採用加鎖和釋放措施,稍有不慎,便會使系統產生管理上的混亂。 而Java從語言一級提供對多線程的支持,這樣,可由語言和運行系統聯合提供對共享數據段的管理功能和同步機制,使得多線程並行程序設計相對比較容易。 2 Java線程的生命周期 每個線程都是和生命周期相聯系的,一個生命周期含有多個狀態,這些狀態間可以互相轉化。 Java的線程的生命周期可以分為4個狀態;創建(new)狀態;可運行(runnable)狀態;不執行(notrunnable)狀態;消亡(dead)狀態。 創建狀態是指創建一個線程對應的對象的過程,Java系統中,些對象都是從Java.lang包內一個稱為Thread的類用關鍵字new創建的。剛創建的線程不能執行,必須向系統進行注冊、分配必要的資源後才能進入可運行狀態,這個步驟是由start操作完成的,而處於可運行狀態的線程也未必一定處於運行中,它有可能由於外部的I/O請求而處於不運行狀態。進入消亡狀態後,此線程就不再存在了。 一個線程創建之後,總是處於其生命周期的4個狀態之一中,線程的狀態表明此線程當前正在進行的活動,而線程的狀態是可以通過程序來進行控制的,就是說,可以對線程進行操作來改變狀態。 這些操作包括啟動(start)、終止(stop)、睡眠(sleep)、掛起(suspend)、恢復(resume)、等待(wait)和通知(notify)。每一個操作都對應了一個方法� 這些方法是由軟體包Java.lang提供的。通過各種操作,線程的4個狀態之間可按圖1所示進行轉換。 2.1 創建(new)狀態 如果創建了一個線程而沒有啟動它,那麼,此線程就處於創建狀態。比如,下述語句執行以後,使系統有了一個處於創建狀態的線程myThread:� Thread myThread=new MyThreadClass(); 其中,MyThreadClass()是Thread的子類,而Thread是由Java系統的Java.lang軟體包提供的。處於創建狀態的線程還沒有獲得應有的資源,所以,這是一個空的線程,線程只有通過啟動後,系統才會為它分配資源。對處於創建狀態的線程可以進行兩種操作: 一是啟動(start)操作,使其進入可運行狀態;二是終止(stop)操作,使其進入消亡狀態。如果進入到消亡狀態,那麼,此後這個線程就不能進入其它狀態,也就是說,它不復存在了。 start方法是對應啟動操作的方法,其具體功能是為線程分配必要的系統資源,將線程設置為可運行狀態,從而可以使系統調度這個線程。 2.2 可運行(runnable)狀態 如果對一個處於創建狀態的線程進行啟動操作,則此線程便進入可運行狀態。比如,用下列語句� myThread.start();� � 則使線程myThread進入可運行狀態。上述語句實質上是調用了線程體即run()方法,注意,run()方法包含在myThread線程中,也就是先由java.lang包的Thread類將run()方法傳遞給子類MyThreadClass(),再通過創建線程由子類MyThreadClass,傳遞給線程myThread。 線程處於可運行狀態只說明它具備了運行條件,但可運行狀態並不一定是運行狀態,因為在單處理器系統中運行多線程程序,實際上在一個時間點只有一個線程在運行,而系統中往往有多個線程同時處於可運行狀態,系統通過快速切換和調度使所有可運行線程共享處理器,造成宏觀上的多線程並發運行。可見,一個線程是否處於運行狀, 除了必須處於可運行狀態外,還取決於系統的調度。 在可運行狀態可以進行多種操作,最通常的是從run()方法正常退出而使線程結束,進入消亡狀態。 此, 還可以有如下操作� 掛起操作,通過調用suspend方法來實現; 睡眠操作,通過調用sleep方法來實現; 等待操作,通過調用wait方法來實現; 退讓操作,通過調用yield方法來實現; 終止操作,通過調用stop方法來實現。 前面三種操作都會使一個處於可運行狀態的線程進入不可運行狀態。比如,仍以myThread線程為例,當其處於可運行狀態後,再用如下語句� myThread.sleep (5000); 則調用sleep方法使myThread線程睡眠5s(5000ms)。這5s內,此線程不能被系統調度運行,只有過5s後,myThread線程才會醒來並自動回到可運行狀態。 如果一個線程被執行掛起操作而轉到不可運行狀態,則必須通過調用恢復(resume)操作,才能使這個線程再回到可運行狀態。 退讓操作是使某個線程把CPU控制權提前轉交給同級優先權的其他線程。 對可運行狀態的線程也可以通過調用stop方法使其進入消亡狀態。 2.3 不可運行(not runnable)狀態 不可運行狀態都是由可運行狀態轉變來的。一個處於可運行狀態的線程,如果遇到掛起(suspend)操作、睡眠(sleep)操作或者等待(wait)操作,就會進入不可運行狀態。 另外,如果一個線程是和I/O操作有關的,那麼,在執行I/O指令時,由於外設速度遠遠低於處理器速度而使線程受到阻, 從而進入不可運行狀態,只有外設完成輸入/輸出之後,才會自動回到可運行狀態。線程進入不可運行狀態後,還可以再回到可運行狀態,通常有三種途徑使其恢復到可運行狀態。 一是自動恢復。通過睡眠(sleep)操作進入不可運行狀態的線程會在過了指定睡眠時間以後自動恢復到可運行狀態,由於I/O阻塞而進入不可運行狀態的線程在外設完成I/O操作後,自動恢復到可運行狀態。 二是用恢復(resume)方法使其恢復。如果一個線程由於掛起(suspend)操作而從可運行狀態進入不可運行狀態,那麼,必須用恢復(resume)操作使其再恢復到可運行狀態。 三是用通知(notify或notifyAll)方法使其恢復。如果一個處於可運行狀態的線程由於等待(wait)操作而轉入不可運行狀態,那麼,必須通過調用notify方法或notifyAll方法才能使其恢復到可運行狀態,採用等待操作往往是由於線程需要等待某個條件變數,當獲得此條件變數後,便可由notify或ontifyAll方法使線程恢復到可運行狀態。 恢復到可運行狀態的每一種途徑都是有針對性的,不能交叉。比如,對由於阻塞而進入不可運行狀態的線程採用恢復操作將是無效的。 在不可運行狀態,也可由終止(stop)操作使其進入消亡狀態。 2.4 消亡(dead)狀態 一個線程可以由其他任何一個狀態通過終止(stop)操作而進入消亡狀態。 線程一旦進入消亡狀態,那它就不再存在了,所以也不可能再轉到其它狀態。 通常,在一個應用程序運行時,如果通過其它外部命令終止當前應用程序,那麼就會調用(stop)方法終止線程。但是,最正常、最常見的途徑是由於線程在可運行狀態正常完成自身的任務而″壽終正寢″,從而進入消亡狀態,這個完成任務的動作是由run方法實現的。 3 Java線程的兩種創建途徑 一種途徑是通過對Thread的繼承來派生一個子類,再由此子類生成一個對象來實現線程的創建,這是比較簡單直接的辦法。Thread類包含在系統API提供的8個軟體包之一Java.lang中,Thread類中包含了很多與線程有關的方, 其中,一個名為run的方法就是用來實現線程行為的。比如:� 1 import java.lang.*� //引用lang包 2 class Mango exteds Thread { 3 public void run() { 4 ...... 5 �} 6 �} 上述程序段中,第1行語句引用軟體包lang,這樣做是為了給編譯器一個信息,從而使後面程序中有關lang包中的方法可直接用方法名,而不必帶前綴「Java.lang」。第2行語句是從lang包Thread派生一個子類Mango, 而這個子類中提供了run方法的實現,這樣,運行時,將由子類Mango 的 run方法置換父類Thread的run方法。 不過這一步還沒有創建線, 必須由子類生成一個對象,並且進行啟動操作,這樣才能得到一個處於可運行狀態的線程。生成對象其實就是完成線程的創建,而啟動是對已創建的線程進行操作。具體語句如下:� Mango t=new Mango(); � t.start(); � 上面先用關鍵字new使線程進入創建狀態,又調用start()方法使線程進入可運行狀態。注意,start()方法是由Thread繼承給子類Mango、然後又在生成對象時由對象t從類Mango得到的。 另一種途徑是通過一個類去繼承介面runnable來實現線程的創建� 而這個類必須提供runnable介面中定義的方法run()的實現。runnable是Java.lang包中的一個介面,在runnable介面中,只定義了一個抽象方法run()。所以,如用這種途徑來創建線程,則應先由一個類連接介面runnable,並且提供run()方法的實現。比如,下面的程序段實現了與介面的連接。 1 public class xyz implements Runnable{ 2 int i; � 3 public voed run(){ 4 while (true){ � 5 System.out.println("Hello"+i++); 6 � } 7 � } 8 � } 然後再創建一個線程� runnable r=new xyz(); � Thread t=new Thread(r); 這種途徑創建線程比第一種途徑靈活。當一個類既需要繼承一個父類又要由此創建一個線程時,由於Java不支持多重繼承,這樣,用第一種途徑將行不通,因為,按此思路創建線程也是以繼承的方法實現的。 於是,就需要一個類既繼承Thread類,又繼承另一個父類。但用介面方法卻能實現這個目標。 4 線程的啟動和終止 Thread的start()方法對應於啟動操作,它完成兩方面的功能:一方面是為線程分配必要的資源,使線程處於可運行狀態,另一方面是調用線程的run()方法置換Thread的中run()方法或者置換runnable中的run()方法來運行線程。 使用start()方法的語句很簡單,即: ThreadName.start(); 下面的程序段先創建並啟動線程myThread, 然後使用sleep()方法讓其睡眠20000ms即20s,使其處於不可運行狀態,過20s後,線程又自動恢復到可運行狀態。 Thread MyThread=new MyThreadClass(); MyThread.start();� � try{ � MyThread.sleep(20000); �} catch(InterrujptedException e){ }

Ⅳ java 多線程是什麼

進程是程序在處理機中的一次運行。一個進程既包括其所要執行的指令,也包括了執行指令所需的系統資源,不同進程所佔用的系統資源相對獨立。所以進程是重量級的任務,它們之間的通信和轉換都需要操作系統付出較大的開銷。

線程是進程中的一個實體,是被系統獨立調度和分派的基本單位。線程自己基本上不擁有系統資源,但它可以與同屬一個進程的其他線程共享進程所擁有的全部資源。所以線程是輕量級的任務,它們之間的通信和轉換只需要較小的系統開銷。

Java支持多線程編程,因此用Java編寫的應用程序可以同時執行多個任務。Java的多線程機制使用起來非常方便,用戶只需關注程序細節的實現,而不用擔心後台的多任務系統。

Java語言里,線程表現為線程類。Thread線程類封裝了所有需要的線程操作控制。在設計程序時,必須很清晰地區分開線程對象和運行線程,可以將線程對象看作是運行線程的控制面板。在線程對象里有很多方法來控制一個線程是否運行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。一旦一個Java程序啟動後,就已經有一個線程在運行。可通過調用Thread.currentThread方法來查看當前運行的是哪一個線程。

Ⅳ java線程鎖死🔒問題,為啥測試程序沒有死鎖啊全部運行出來了,jdk8

死鎖的前提是。兩個人吃飯,都需要需要刀和叉但又只有一套, 其中一個人拿了叉,另一個拿了刀,就出現互相等待的情況。你的obj1和obj2就相當於刀叉,但是你並沒有說拿了叉,還會繼續去拿刀。你的代碼敏老寬意思是,t1拿叉,然後放下叉,然後去拿刀。t2做的動作橋亮是拿刀,放下刀,再拿叉。他們之間的動作並不會導致矛盾。我給你改成下面這樣了,但是這樣也不一定會導致死鎖,因為你沒有做循環,只做一次操作,萬一t1跑的快,瞬間就做完所有操作了,也不會阻塞t2.
if(Thread.currentThread().getName().equals("t1"含或)){
synchronized(obj2){
System.out.println("線程1鎖定obj2");
synchronized(obj1){
System.out.println("線程1鎖定obj1");
}
}

}else if(Thread.currentThread().getName().equals("t2")){
synchronized(obj1){
System.out.println("線程2鎖定obj1");
synchronized(obj2){
System.out.println("線程2鎖定obj2");
}
}
}

Ⅵ java多線程是怎麼回事

進程是程序在處理機中的一次運行。一個進程既包括其所要執行的指令,也包括了執行指令所需的系統資源,不同進程所佔用的系統資源相對獨立。所以進程是重量級的任務,它們之間的通信和轉換都需要操作系統付出較大的開銷。
線程是進程中的一個實體,是被系統獨立調度和分派的基本單位。線程自己基本上不擁有系統資源,但它可以與同屬一個進程的其他線程共享進程所擁有的全部資源。所以線程是輕量級的任務,它們之間的通信和轉換只需要較小的系統開銷。
Java支持多線程編程,因此用Java編寫的應用程序可以同時執行多個任務。Java的多線程機制使用起來非常方便,用戶只需關注程序細節的實現,而不用擔心後台的多任務系統。
Java語言里,線程表現為線程類。Thread線程類封裝了所有需要的線程操作控制。在設計程序時,必須很清晰地區分開線程對象和運行線程,可以將線程對象看作是運行線程的控制面板。在線程對象里有很多方法來控制一個線程是否運行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。一旦一個Java程序啟動後,就已經有一個線程在運行。可通過調用Thread.currentThread方法來查看當前運行的是哪一個線程。

class ThreadTest{
public static void main(String args[]){
Thread t = Thread.currentThread();
t.setName("單線程"); //對線程取名為"單線程"
t.setPriority(8);
//設置線程優先順序為8,最高為10,最低為1,默認為5
System.out.println("The running thread: " + t);
// 顯示線程信息
try{
for(int i=0;i<3;i++){
System.out.println("Sleep time " + i);
Thread.sleep(100); // 睡眠100毫秒
}
}catch(InterruptedException e){// 捕獲異常
System.out.println("thread has wrong");
}
}
}

多線程的實現方法
繼承Thread類
可通過繼承Thread類並重寫其中的run()方法來定義線程體以實現線程的具體行為,然後創建該子類的對象以創建線程。
在繼承Thread類的子類ThreadSubclassName中重寫run()方法來定義線程體的一般格式為:
public class ThreadSubclassName extends Thread{
public ThreadSubclassName(){
..... // 編寫子類的構造方法,可預設
}
public void run(){
..... // 編寫自己的線程代碼
}
}
用定義的線程子類ThreadSubclassName創建線程對象的一般格式為:
ThreadSubclassName ThreadObject =
new ThreadSubclassName();
然後,就可啟動該線程對象表示的線程:
ThreadObject.start(); //啟動線程

應用繼承類Thread的方法實現多線程的程序。本程序創建了三個單獨的線程,它們分別列印自己的「Hello World!」。
class ThreadDemo extends Thread{
private String whoami;
private int delay;
public ThreadDemo(String s,int d){
whoami=s;
delay=d;
}
public void run(){
try{
sleep(delay);
}catch(InterruptedException e){ }
System.out.println("Hello World!" + whoami
+ " " + delay);
}
}
public class MultiThread{
public static void main(String args[]){
ThreadDemo t1,t2,t3;
t1 = new ThreadDemo("Thread1",
(int)(Math.random()*2000));
t2 = new ThreadDemo("Thread2",
(int)(Math.random()*2000));
t3 = new ThreadDemo("Thread3",
(int)(Math.random()*2000));
t1.start();
t2.start();
t3.start();
}
}

實現Runnable介面
編寫多線程程序的另一種的方法是實現Runnable介面。在一個類中實現Runnable介面(以後稱實現Runnable介面的類為Runnable類),並在該類中定義run()方法,然後用帶有Runnable參數的Thread類構造方法創建線程。
創建線程對象可用下面的兩個步驟來完成:
(1)生成Runnable類ClassName的對象
ClassName RunnableObject = new ClassName();
(2)用帶有Runnable參數的Thread類構造方法創建線程對象。新創建的線程的指針將指向Runnable類的實例。用該Runnable類的實例為線程提供 run()方法---線程體。
Thread ThreadObject = new Thread(RunnableObject);
然後,就可啟動線程對象ThreadObject表示的線程:
ThreadObject.start();
在Thread類中帶有Runnable介面的構造方法有:
public Thread(Runnable target);
public Thread(Runnable target, String name);
public Thread(String name);
public Thread(ThreadGroup group,Runnable target);
public Thread(ThreadGroup group,Runnable target,
String name);
其中,參數Runnable target表示該線程執行時運行target的run()方法,String name以指定名字構造線程,ThreadGroup group表示創建線程組。
用Runnable介面實現的多線程。
class TwoThread implements Runnable{
TwoThread(){
Thread t1 = Thread.currentThread();
t1.setName("第一主線程");
System.out.println("正在運行的線程: " + t1);
Thread t2 = new Thread(this,"第二線程");
System.out.println("創建第二線程");
t2.start();
try{
System.out.println("第一線程休眠");
Thread.sleep(3000);
}catch(InterruptedException e){
System.out.println("第一線程有錯");
}
System.out.println("第一線程退出");
}
public void run(){
try{
for(int i = 0;i < 5;i++){
System.out.println(「第二線程的休眠時間:」
+ i);
Thread.sleep(1000);
}
}catch(InterruptedException e){
System.out.println("線程有錯");
}
System.out.println("第二線程退出");
}
public static void main(String args[]){
new TwoThread();
}
}
程序運行結果如下:
正在運行的線程: Thread[第一主線程,5,main
創建第二線程
第一線程休眠
第二線程的休眠時間:0
第二線程的休眠時間:1
第二線程的休眠時間:2
第一線程退出
第二線程的休眠時間:3
第二線程的休眠時間:4
第二線程退出

Ⅶ java8 中concurrenthashmap數據結構和HashMap一樣,且線程安全 為什麼還要HashMap

java阻塞斗旅隊列應用於生產者消費者模式、消息傳遞、並行任務執行和相關並發設計的大多數常見使用上下文。

BlockingQueue在Queue介面基礎上提供了額外的兩種類型的操作,分別是獲取元素時等待隊列變為非空和添加元素時等待空間變為可用。

BlockingQueue新增操作的四種形式:

插入操作是指向隊列中添加一個元素,至於元素存放的位置與具體隊列的實現有關。移除操作將會移除隊列的頭部元素,並將這個移除的元素作為返回值反饋給調用者。檢查操作是指返回隊列的頭元素給調用者,隊列不對這個頭元素進行刪除處理。

拋出異常形式的操作,在隊列已滿的情況下,調用add方法將會拋出IllegalStateException異常。如果調用remove方法時,隊列已經為空,則拋出一個NoSuchElementException異常。(實際上,remove方法還可以附帶一個參數,用來刪除隊列中的指定元素,如果這個元素不存在,也會拋出NoSuchElementException異常)。如果調用element檢查頭元素,隊列為空時,將會拋出NoSuchElementException異常。

特殊值操作與拋出異常不同,在出錯的時候,返回一個空指針,而不會拋出異常。

阻塞形式的操作,調用put方法時,如果隊列已滿,則調用線程阻塞等待其它線程從隊列中取出元素。調用take方法時,如果阻塞隊列已經為空,則調用線程阻塞等待其它線程向隊列添加新元素。

超時形式操作,在阻塞的基礎上添加一個超時限制,如果等待時間超過指定值,拋出InterruptedException。

阻塞隊列實現了Queue介面,而Queue介面實現了Collection介面,因此BlockingQueue也提供了remove(e)操作,即從隊列中移除任意指定元素,但是這個操作往往不會按預期那樣高效的執行,所以應當盡量少的使用這種操作。

阻塞隊列與並發隊列(例如ConcurrentLinkQueue)都是線程安全的,但使用的場合不同。

Graphic3-1給出了阻塞隊列的介面方法,Graphic3-2給出了阻塞隊列的實現類結構。

Graphic 3-1 BlockingQueue介面



Graphic3-2阻塞隊列的實現類


3.1.1 ArrayBlockingQueue類

一個以數組為基礎的有界阻塞隊列,此隊列按照先進先出原則對元素進行排序。隊列頭部元素是隊列中存在時間最長的元素,隊列尾部是存在時間最短的元素,新元素將會被插入到隊列尾部。隊列從頭部開始獲取元素。

ArrayBlockingQueue是「有界緩存區」模型的一種實現,一旦創建了這樣的緩存區,就不能再改變緩沖區的大小。ArrayBlockingQueue的一個特點是,必須在創建的時候指定隊列的大小。當緩沖區已滿,則需要阻塞新增的插入操作,同理,當緩沖區已空需要阻塞新增的提取操作。

ArrayBlockingQueue是使用的是循環隊列方法實現的,對ArrayBlockingQueue的相關操作的時間復雜度,可以參考循環隊列進行空老凳分析。

3.1.2 LinkedBlockingQueue

一種通過鏈表實現的阻塞隊列,支持先進先出。隊列的頭部是隊列中保持時間最長的元素含跡,隊列的尾部是保持時間最短的元素。新元素插入隊列的尾部。可選的容量設置可以有效防止隊列過於擴張造成系統資源的過多消耗,如果不指定隊列容量,隊列默認使用Integer.MAX_VALUE。LinkedBlockingQueue的特定是,支持無限(理論上)容量。

3.1.3 PriorityBlockingQueue

PriorityBlockingQueue是一種基於優先順序進行排隊的無界隊列。隊列中的元素按照其自然順序進行排列,或者根據提供的Comparator進行排序,這與構造隊列時,提供的參數有關。

使用提取方法時,隊列將返回頭部,具有最高優先順序(或最低優先順序,這與排序規則有關)的元素。如果多個元素具有相同的優先順序,則同等優先順序間的元素獲取次序無特殊說明。

優先順序隊列使用的是一種可擴展的數組結構,一般可以認為這個隊列是無界的。當需要新添加一個元素時,如果此時數組已經被填滿,優先隊列將會自動擴充當前數組(一般認為是,先分配一個原數組一定倍數空間的數組,之後將原數組中的元素拷貝到新分配的數組中,釋放原數組的空間)。

如果使用優先順序隊列的iterator變數隊列時,不保證遍歷次序按照優先順序大小進行。因為優先順序隊列使用的是堆結構。如果需要按照次序遍歷需要使用Arrays.sort(pq.toArray())。關於堆結構的相關演算法,請查考數據結構相關的書籍。

在PriorityBlockingQueue的實現過程中聚合了PriorityQueue的一個實例,並且優先隊列的操作完全依賴與PriorityQueue的實現。在PriorityQueue中使用了一個一維數組來存儲相關的元素信息。一維數組使用最小堆演算法進行元素添加。

Graphic3-3PriorityBlockingQueue的類關系


3.2.1.1 TreeMap

盡管TreeMap不是線程安全的,但是基於其數據結構的復雜性和方便對比說明,還是在這里簡單提一下。TreeMap實現了SortedMap介面。TreeMap使用的是紅黑樹(這是高等數據結構中的一種),在紅黑樹演算法中,當添加或刪除節點時,需要進行旋轉調整樹的高度。使用紅黑樹演算法具有較好的操作特性,插入、刪除、查找都能在O(log(n))時間內完成。紅黑樹理論和實現是很復雜的,但可以帶來較高的效率,因此在許多場合也得到了廣泛使用。紅黑樹的一個缺陷在於,可變操作很可能影響到整棵樹的結構,針對修改的局部效果不好。相關演算法請參考http://blog.sina.com.cn/s/blog_616e189f0100qgcm.html。

TreeMap不是線程安全的,如果同時有多個線程訪問同一個Map,並且其中至少有一個線程從結構上修改了該映射,則必須使用外部同步。可以使用Collections.synchronizedSortedMap方法來包裝該映射。(注意使用包裝器包裝的SortMap是線程安全的,但不是並發的,效率上很可能遠遠不及ConcurrentSkipListMap,因此使用包裝器的方法並不十分推薦,有人認為那是一種過時的做法。包裝器使用了鎖機制控制對Map的並發訪問,但是這種加鎖的粒度可能過大,很可能影響並發度)。

3.2.1.2 ConcurrentSkipListMap

另外一種實現了SortedMap介面的映射表是ConcurrentSkipListMap。ConcurrentSkipListMap提供了一種線程安全的並發訪問的排序映射表。SkipList(跳錶)結構,在理論上能夠在O(log(n))時間內完成查找、插入、刪除操作。SkipList是一種紅黑樹的替代方案,由於SkipList與紅黑樹相比無論從理論和實現都簡單許多,所以得到了很好的推廣。SkipList是基於一種統計學原理實現的,有可能出現最壞情況,即查找和更新操作都是O(n)時間復雜度,但從統計學角度分析這種概率極小。Graphic3-6給出了SkipList的數據表示示例。有關skipList更多的說明可以參考:http://blog.csdn.net/caoeryingzi/archive/2010/11/18/6018070.aspx和http://en.wikipedia.org/wiki/Skip_list這里不在累述。希望讀者自行學習。

使用SkipList類型的數據結構更容易控制多線程對集合訪問的處理,因為鏈表的局部處理性比較好,當多個線程對SkipList進行更新操作(指插入和刪除)時,SkipList具有較好的局部性,每個單獨的操作,對整體數據結構影響較小。而如果使用紅黑樹,很可能一個更新操作,將會波及整個樹的結構,其局部性較差。因此使用SkipList更適合實現多個線程的並發處理。在非多線程的情況下,應當盡量使用TreeMap,因為似乎紅黑樹結構要比SkipList結構執行效率略優(無論是時間復雜度還是空間復雜度,作者沒有做夠測試,只是直覺)。此外對於並發性相對較低的並行程序可以使用Collections.synchronizedSortedMap將TreeMap進行包裝,也可以提供較好的效率。對於高並發程序,應當使用ConcurrentSkipListMap,能夠提供更高的並發度。

所以在多線程程序中,如果需要對Map的鍵值進行排序時,請盡量使用ConcurrentSkipListMap,可能得到更好的並發度。

注意,調用ConcurrentSkipListMap的size時,由於多個線程可以同時對映射表進行操作,所以映射表需要遍歷整個鏈表才能返回元素個數,這個操作是個O(log(n))的操作。

Graphic3-6 SkipList示例


3.2.1.3 HashMap類

對Map類的另外一個實現是HashMap。HashMap使用Hash表數據結構。HashMap假定哈希函數能夠將元素適當的分布在各桶之間,提供一種接近O(1)的查詢和更新操作。但是如果需要對集合進行迭代,則與HashMap的容量和桶的大小有關,因此HashMap的迭代效率不會很高(尤其是你為HashMap設置了較大的容量時)。

與HashMap性能有影響的兩個參數是,初始容量和載入因子。容量是哈希表中桶的數量,初始容量是哈希表在創建時的容量。載入因子是哈希表在容器容量被自動擴充之前,HashMap能夠達到多滿的一種程度。當hash表中的條目數超出了載入因子與當前容量的乘積時,Hash表需要進行rehash操作,此時Hash表將會擴充為以前兩倍的桶數,這個擴充過程需要進行完全的拷貝工作,效率並不高,因此應當盡量避免。合理的設置Hash表的初始容量和載入因子會提高Hash表的性能。HashMap自身不是線程安全的,可以通過Collections的synchronizedMap方法對HashMap進行包裝。

3.2.1.4 ConcurrentHashMap類

ConcurrentHashMap類實現了ConcurrentMap介面,並提供了與HashMap相同的規范和功能。實際上Hash表具有很好的局部可操作性,因為對Hash表的更新操作僅會影響到具體的某個桶(假設更新操作沒有引發rehash),對全局並沒有顯著影響。因此ConcurrentHashMap可以提供很好的並發處理能力。可以通過concurrencyLevel的設置,來控制並發工作線程的數目(默認為16),合理的設置這個值,有時很重要,如果這個值設置的過高,那麼很有可能浪費空間和時間,使用的值過低,又會導致線程的爭用,對數量估計的過高或過低往往會帶來明顯的性能影響。最好在創建ConcurrentHashMap時提供一個合理的初始容量,畢竟rehash操作具有較高的代價。

3.2.2 ConcurrentSkipListSet類

實際上Set和Map從結構來說是很像的,從底層的演算法原理分析,Set和Map應當屬於同源的結構。所以Java也提供了TreeSet和ConcurrentSkipListSet兩種SortedSet,分別適合於非多線程(或低並發多線程)和多線程程序使用。具體的演算法請參考前述的Map相關介紹,這里不在累述。

3.2.3 CopyOnWriteArrayList類

CopyOnWriteArrayList是ArrayList的一個線程安全的變體,其中對於所有的可變操作都是通過對底層數組進行一次新的復制來實現的。

由於可變操作需要對底層的數據進行一次完全拷貝,因此開銷一般較大,但是當遍歷操作遠遠多於可變操作時,此方法將會更有效,這是一種被稱為「快照」的模式,數組在迭代器生存期內不會發生更改,因此不會產生沖突。創建迭代器後,迭代器不會反映列表的添加、移除或者更改。不支持在迭代器上進行remove、set和add操作。CopyOnWriteArraySet與CopyOnWriteArrayList相似,只不過是Set類的一個變體。

3.2.3 Collections提供的線程安全的封裝

Collections中提供了synchronizedCollection、synchronizedList、synchronizedMap、synchronizedSet、synchronizedSortedMap、synchronizedSortedMap等方法可以完成多種集合的線程安全的包裝,如果在並發度不高的情況下,可以考慮使用這些包裝方法,不過由於Concurrent相關的類的出現,已經不這么提倡使用這些封裝了,這些方法有些人稱他們為過時的線程安全機制。

3.2.4簡單總結

提供線程安全的集合簡單概括分為三類,首先,對於並發性要求很高的需求可以選擇以Concurrent開頭的相應的集合類,這些類主要包括:ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentSkipListMap、ConcurrentSkipSet。其次對於可變操作次數遠遠小於遍歷的情況,可以使用CopyOnWriteArrayList和CopyOnWriteArraySet類。最後,對於並發規模比較小的並行需求可以選擇Collections類中的相應方法對已有集合進行封裝。

此外,本章還對一些集合類的底層實現進行簡單探討,對底層實現的了解有利於對何時使用何種方式作出正確判斷。希望大家能夠將涉及到原理(主要有循環隊列、堆、HashMap、紅黑樹、SkipList)進行仔細研究,這樣才能更深入了解Java為什麼這樣設計類庫,在什麼情況使用,應當如何使用。

Ⅷ Java線程優先順序問題,為什麼我設置了t1的優先順序為8,運行結果卻是t2優先

這個優先順序設置只能提高該線程的優先概率,不能讓一個線程執行完才執行下一個。

Ⅸ 在Java 中多線程的實現方法有哪些,如何使用

Java多線程的創建及啟動

Java中線程的創建常見有如三種基本形式

1.繼承Thread類,重寫該類的run()方法。

復制代碼

1 class MyThread extends Thread {

2

3 private int i = 0;

4

5 @Override

6 public void run() {

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

8 System.out.println(Thread.currentThread().getName() + " " + i);

9 }

10 }

11 }

復制代碼

復制代碼

1 public class ThreadTest {

2

3 public static void main(String[] args) {

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

5 System.out.println(Thread.currentThread().getName() + " " + i);

6 if (i == 30) {

7 Thread myThread1 = new MyThread(); // 創建一個新的線程 myThread1 此線程進入新建狀態

8 Thread myThread2 = new MyThread(); // 創建一個新的線程 myThread2 此線程進入新建狀態

9 myThread1.start(); // 調用start()方法使得線程進入就緒狀態

10 myThread2.start(); // 調用start()方法使得線程進入就緒狀態

11 }

12 }

13 }

14 }

復制代碼

如上所示,繼承Thread類,通過重寫run()方法定義了一個新的線程類MyThread,其中run()方法的方法體代表了線程需要完成的任務,稱之為線程執行體。當創建此線程類對象時一個新的線程得以創建,並進入到線程新建狀態。通過調用線程對象引用的start()方法,使得該線程進入到就緒狀態,此時此線程並不一定會馬上得以執行,這取決於CPU調度時機。

2.實現Runnable介面,並重寫該介面的run()方法,該run()方法同樣是線程執行體,創建Runnable實現類的實例,並以此實例作為Thread類的target來創建Thread對象,該Thread對象才是真正的線程對象。

復制代碼

1 class MyRunnable implements Runnable {

2 private int i = 0;

3

4 @Override

5 public void run() {

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

7 System.out.println(Thread.currentThread().getName() + " " + i);

8 }

9 }

10 }

復制代碼

復制代碼

1 public class ThreadTest {

2

3 public static void main(String[] args) {

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

5 System.out.println(Thread.currentThread().getName() + " " + i);

6 if (i == 30) {

7 Runnable myRunnable = new MyRunnable(); // 創建一個Runnable實現類的對象

8 Thread thread1 = new Thread(myRunnable); // 將myRunnable作為Thread target創建新的線程

9 Thread thread2 = new Thread(myRunnable);

10 thread1.start(); // 調用start()方法使得線程進入就緒狀態

11 thread2.start();

12 }

13 }

14 }

15 }

復制代碼

相信以上兩種創建新線程的方式大家都很熟悉了,那麼Thread和Runnable之間到底是什麼關系呢?我們首先來看一下下面這個例子。

復制代碼

1 public class ThreadTest {

2

3 public static void main(String[] args) {

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

5 System.out.println(Thread.currentThread().getName() + " " + i);

6 if (i == 30) {

7 Runnable myRunnable = new MyRunnable();

8 Thread thread = new MyThread(myRunnable);

9 thread.start();

10 }

11 }

12 }

13 }

14

15 class MyRunnable implements Runnable {

16 private int i = 0;

17

18 @Override

19 public void run() {

20 System.out.println("in MyRunnable run");

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

22 System.out.println(Thread.currentThread().getName() + " " + i);

23 }

24 }

25 }

26

27 class MyThread extends Thread {

28

29 private int i = 0;

30

31 public MyThread(Runnable runnable){

32 super(runnable);

33 }

34

35 @Override

36 public void run() {

37 System.out.println("in MyThread run");

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

39 System.out.println(Thread.currentThread().getName() + " " + i);

40 }

41 }

42 }

復制代碼

同樣的,與實現Runnable介面創建線程方式相似,不同的地方在於

1 Thread thread = new MyThread(myRunnable);

那麼這種方式可以順利創建出一個新的線程么?答案是肯定的。至於此時的線程執行體到底是MyRunnable介面中的run()方法還是MyThread類中的run()方法呢?通過輸出我們知道線程執行體是MyThread類中的run()方法。其實原因很簡單,因為Thread類本身也是實現了Runnable介面,而run()方法最先是在Runnable介面中定義的方法。

1 public interface Runnable {

2

3 public abstract void run();

4

5 }

我們看一下Thread類中對Runnable介面中run()方法的實現:

復制代碼

@Override

public void run() {

if (target != null) {

target.run();

}

}

復制代碼

也就是說,當執行到Thread類中的run()方法時,會首先判斷target是否存在,存在則執行target中的run()方法,也就是實現了Runnable介面並重寫了run()方法的類中的run()方法。但是上述給到的列子中,由於多態的存在,根本就沒有執行到Thread類中的run()方法,而是直接先執行了運行時類型即MyThread類中的run()方法。

3.使用Callable和Future介面創建線程。具體是創建Callable介面的實現類,並實現clall()方法。並使用FutureTask類來包裝Callable實現類的對象,且以此FutureTask對象作為Thread對象的target來創建線程。

看著好像有點復雜,直接來看一個例子就清晰了。

復制代碼

1 public class ThreadTest {

2

3 public static void main(String[] args) {

4

5 Callable<Integer> myCallable = new MyCallable(); // 創建MyCallable對象

6 FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask來包裝MyCallable對象

7

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

9 System.out.println(Thread.currentThread().getName() + " " + i);

10 if (i == 30) {

11 Thread thread = new Thread(ft); //FutureTask對象作為Thread對象的target創建新的線程

12 thread.start(); //線程進入到就緒狀態

13 }

14 }

15

16 System.out.println("主線程for循環執行完畢..");

17

18 try {

19 int sum = ft.get(); //取得新創建的新線程中的call()方法返回的結果

20 System.out.println("sum = " + sum);

21 } catch (InterruptedException e) {

22 e.printStackTrace();

23 } catch (ExecutionException e) {

24 e.printStackTrace();

25 }

26

27 }

28 }

29

30

31 class MyCallable implements Callable<Integer> {

32 private int i = 0;

33

34 // 與run()方法不同的是,call()方法具有返回值

35 @Override

36 public Integer call() {

37 int sum = 0;

38 for (; i < 100; i++) {

39 System.out.println(Thread.currentThread().getName() + " " + i);

40 sum += i;

41 }

42 return sum;

43 }

44

45 }

復制代碼

首先,我們發現,在實現Callable介面中,此時不再是run()方法了,而是call()方法,此call()方法作為線程執行體,同時還具有返回值!在創建新的線程時,是通過FutureTask來包裝MyCallable對象,同時作為了Thread對象的target。那麼看下FutureTask類的定義:

1 public class FutureTask<V> implements RunnableFuture<V> {

2

3 //....

4

5 }

1 public interface RunnableFuture<V> extends Runnable, Future<V> {

2

3 void run();

4

5 }

於是,我們發現FutureTask類實際上是同時實現了Runnable和Future介面,由此才使得其具有Future和Runnable雙重特性。通過Runnable特性,可以作為Thread對象的target,而Future特性,使得其可以取得新創建線程中的call()方法的返回值。

執行下此程序,我們發現sum = 4950永遠都是最後輸出的。而「主線程for循環執行完畢..」則很可能是在子線程循環中間輸出。由CPU的線程調度機制,我們知道,「主線程for循環執行完畢..」的輸出時機是沒有任何問題的,那麼為什麼sum =4950會永遠最後輸出呢?

原因在於通過ft.get()方法獲取子線程call()方法的返回值時,當子線程此方法還未執行完畢,ft.get()方法會一直阻塞,直到call()方法執行完畢才能取到返回值。

上述主要講解了三種常見的線程創建方式,對於線程的啟動而言,都是調用線程對象的start()方法,需要特別注意的是:不能對同一線程對象兩次調用start()方法。

你好,本題已解答,如果滿意

請點右下角「採納答案」。


Ⅹ 四核八線程夠Java軟體開發的學生用嗎

您好,四核八線程夠用了,軟體開發對配置的要求不是特別高。不過內存最好多一點。

閱讀全文

與java8線程相關的資料

熱點內容
微信聊天界面源碼 瀏覽:24
seo競價推廣點擊價格演算法公式 瀏覽:319
框架結構可以加密嗎 瀏覽:218
python編譯器怎麼清除 瀏覽:73
linux全局socks代理 瀏覽:611
php微信抽獎 瀏覽:771
壓縮演算法嵌入式移植 瀏覽:531
php新手小例子 瀏覽:233
按照醫生的演算法一周是幾天 瀏覽:805
三次b樣條曲線演算法 瀏覽:924
java7特性 瀏覽:555
愛山東app小學報名怎麼知道報沒報上 瀏覽:458
android獲取wifi信號 瀏覽:133
娜拉美妝app怎麼使用 瀏覽:760
有了源碼要買伺服器嗎 瀏覽:365
app怎麼查看自己的存款利息 瀏覽:515
碧藍安卓與b站有什麼區別 瀏覽:342
php靜態塊 瀏覽:719
ftpmget命令 瀏覽:475
源碼時代怎樣 瀏覽:415