第一篇:【java總結】多線程(基礎篇)
【java總結】多線程(基礎篇)
Java的線程分為5種狀態:創建、就緒、運行、阻塞和死亡。
創建:
在java種創建線程的方式有兩種,一種是通過繼承Thread類并且重寫run方法,run方法中執行的代碼便是線程執行的代碼。另一種是通過實現Runnable接口,并將該接口實例傳入一個Thread實例。通過對Thread的引用調用start()方法,即可讓線程進入就緒狀態。如果直接調用run方法,并不會生成線程,而是在當前線程中把run()當做一個普通方法執行。[java] view plain copy public class Thread1 extends Thread{
/*
* 實現線程的方法一:通過繼承Thread并覆蓋run()方法來實現多線程。
*/
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+“線程開始!”);
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+“ ”+i);
try{
sleep((int)Math.random()*10);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+“線程結束!”);
}
}
[java] view plain copy public class Thread2 implements Runnable{
/*
* 實現線程的方法二:通過實現Runnable接口來實現多線程
* 實現Runnable接口比繼承Thread類所具有的優勢:
* 1):適合多個相同的程序代碼的線程去處理同一個資源
* 2):可以避免java中的單繼承的限制
* 3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立
*/
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+“線程開始!”);
for(iwww.tmdps.cnnt i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+“ ”+i);
try{
Thread.sleep((int)Math.random()*10);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+“線程結束!”);
}
}
就緒:
處于就緒狀態的線程隨時可以被JVM的線程調度器調度,進入運行狀態。對于處于就緒狀態的線程,我們并不能對他們被調度的順序進行任何估計,也就是說,線程的執行順序是不可預測的。處于運行狀態的線程,通過調用yield()方法,可以返回到就緒狀態,然而它有可能瞬間被再次調度。yield()方法把運行機會讓給了同等優先級的其他線程。
[java] view plain copy public class ThreadYield extends Thread{
@Override
public void run(){
for(int i = 1;i <= 50;i++){
System.out.println(“" +Thread.currentThread().getName()+ ”-----“ + i);
// 當i==25時,該線程就會把CPU時間讓掉,讓其他或者自己的線程執行(也就是誰先搶到誰執行)
if(i==25){
this.yield();
}
}
}
}
[java] view plain copy public class ThreadYieldTest {
/*
* Thread.yield():暫停當前正在執行的線程對象,并執行其他線程。
* 該方法讓當前線程回到可運行狀態,以允許其他具有相同優先級的線程獲得運行機會。
* 但是實際中無法保證yield()達到讓步目的,因為當前線程有可能被線程調度程序再次選中。
*/
public static void main(String[] args){
ThreadYield thread1=new ThreadYield();
ThreadYield thread2=new ThreadYield();
thread1.start();
thread2.start();
}
}
運行:
處于運行狀態的線程隨時有可能被線程調度器換下,進入到就緒狀態。想要規定線程的順序,需要調用join方法,對某個線程 的調用join方法,則主線程會阻塞到該線程執行完后再繼續執行。或者使用一種叫做鎖的機制(下文會提及)。當一個線程完成它run()里面的所有工作時,線程會自動死亡。調用sleep(),線程會進入休眠,并且在一段時間內不會被再度調用。睡眠時間過后,線程才再次進入就緒隊列中。
[java] view plain copy public class ThreadJoinTest {
/*
* join是Thread類的一個方法,作用是等待該線程終止。例如對子線程A調用join()方法,* 主線程將等待子線程A終止后才能繼續后面的代碼。
*/
public static void main(String[] args){
System.out.println(”主線程開始!“);
Thread1 thread1=new Thread1();
Thread1 thread2=new Thread1();
thread1.start();
thread2.start();
try{
thread1.join();
}catch(InterruptedException e){
e.printStackTrace();
}
try{
thread2.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(”主線程結束!“);
} }
死亡:
線程因為代碼執行完畢而正常結束自身線程,或者因為某些異常而結束線程。
[java] view plain copy public class ThreadInterrupt extends Thread{
/*
* wait()和sleep()都可以通過interrupt()方法 打斷線程的暫停狀態,從而使線程立刻拋出Interruptedwww.tmdps.cnt i=0;i<3;i++){
new Thread(new Runnable(){
@Override
public void run(){
ThreadSynchronizedTest x=new ThreadSynchronizedTest();
System.out.println(”value=“+x.getNext());
System.out.println(”value=“+x.getNext2());
System.out.println(”value=“+x.getNext3());
}
}).start();
}
}
}
阻塞:
阻塞跟Obj.wait(),Obj.notify()方法有關。當調用wait方法時,線程釋放對象鎖,進入阻塞狀態,直到其他線程喚醒它。
Obj.wait(),與Obj.notify()必須要與synchronized(Obj)一起使用
Obj.notify()作用:對對象鎖的喚醒操作。notify()調用后,并不是馬上就釋放對象鎖的,而 是在相應的synchronized(){}語句塊執行結束,自動釋放鎖后,JVM會在wait()對象鎖的線 程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。
Obj.wait()作用:線程在獲取對象鎖后,主動釋放對象鎖,同時本線程休眠,直到有其它線程調用
對象的notify()喚醒該線程,才能繼續獲取對象鎖,并繼續執行。下面我們通過一道題目來加深理解。
問題:建立三個線程,A線程打印10次A,B線程打印10次B,C線程打印10次C,要求線程同時運行,交替打印10次ABC。代碼如下:
[java] viewww.tmdps.cnw plain copy public class ThreadPrintABCTest {
/*
* 建立三個線程,A線程打印10次A,B線程打印10次B,C線程打印10次C,要求線程同時運行,交替打印10次ABC。
*
*/
public static void main(String[] args)throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
ThreadPrintABC pa = new ThreadPrintABC(”A“, c, a);
ThreadPrintABC pb = new ThreadPrintABC(”B“, a, b);
ThreadPrintABC pc = new ThreadPrintABC(”C“, b, c);
new Thread(pa).start();
Thread.sleep(100);//確保按順序A、B、C執行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
[java] view plain copy public class ThreadPrintABC implements Runnable{
private String data;
private Object pre;
private Object self;
public ThreadPrintABC(String data,Object pre,Object self){
this.data=data;
this.pre=pre;
this.self=self;
}
@Override
public void run(){
int count=10;
while(count>0){
synchronized(pre){
synchronized(self){
if(data==”C"){
System.out.println(data);
}else{
System.out.print(data);
}
count--;
self.notify();
}
try{
pre.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
}
死鎖:
兩個或兩個以上的線程在執行過程當中,由于競爭資源或者彼此之間通信而造成的一種阻塞現象。比如,當線程A調用wait()方法等待線程B的喚醒,而線程B同時也調用wait方法等待線程A的喚醒,這時兩個線程將陷入僵持狀態,永遠處在阻塞狀態,成為死鎖進程,即兩個線程永遠也不會被執行。
sleep方法與wait方法的區別及細節:
sleep()睡眠時,保持對象鎖,仍然占有該鎖;而wait()睡眠時,釋放對象鎖。sleep()使當前線程進入停滯狀態(阻塞當前線程),讓出CPU的使用、目的是不讓當前線程獨自霸占該進程所獲的CPU資源,以留一定時間給其他線程執行的機會;sleep()是Thread類的Static(靜態)的方法;因此他不能改變對象的機鎖,所以當在一個Synchronized塊中調用Sleep()方法是,線程雖然休眠了,但是對象的鎖并木有被釋放,其他線程無法訪問這個對象(即使睡著也持有對象鎖)。在sleep()休眠時間期滿后,該線程不一定會立即執行,這是因為其它線程可能正在運行而且沒有被調度為放棄執行,除非此線程具有更高的優先級。
wait()方法是Object類里的方法;當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖(暫時失去鎖,wait(long timeout)超時時間到后還需要返還對象鎖);其他線程可以訪問;
wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的線程。wiat()必須放在synchronizedblock中,否則扔出”java.lang.IllegalMonitorStateException“異常。
第二篇:Java多線程編程總結
Java多線程編程總結
2007-05-17 11:21:59 標簽:java 多線程
原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處、作者信息和本聲明。否則將追究法律責任。http://lavasoft.blog.51cto.com/62575/27069
Java多線程編程總結
下面是Java線程系列博文的一個編目:
Java線程:概念與原理 Java線程:創建與啟動
Java線程:線程棧模型與線程的變量 Java線程:線程狀態的轉換 Java線程:線程的同步與鎖 Java線程:線程的交互 Java線程:線程的調度-休眠 Java線程:線程的調度-優先級 Java線程:線程的調度-讓步 Java線程:線程的調度-合并 Java線程:線程的調度-守護線程 Java線程:線程的同步-同步方法 Java線程:線程的同步-同步塊
Java線程:并發協作-生產者消費者模型 Java線程:并發協作-死鎖 Java線程:volatile關鍵字 Java線程:新特征-線程池
Java線程:新特征-有返回值的線程 Java線程:新特征-鎖(上)Java線程:新特征-鎖(下)Java線程:新特征-信號量 Java線程:新特征-阻塞隊列 Java線程:新特征-阻塞棧 Java線程:新特征-條件變量 Java線程:新特征-原子量 Java線程:新特征-障礙器 Java線程:大總結
----
下面的內容是很早之前寫的,內容不夠充實,而且是基于Java1.4的內容,Java5之后,線程并發部分擴展了相當多的內容,因此建議大家看上面的系列文章的內容,與時俱進,跟上Java發展的步伐。----
一、認識多任務、多進程、單線程、多線程 要認識多線程就要從操作系統的原理說起。
以前古老的DOS操作系統(V 6.22)是單任務的,還沒有線程的概念,系統在每次只能做一件事情。比如你在copy東西的時候不能rename文件名。為了提高系統的利用效率,采用批處理來批量執行任務。
現在的操作系統都是多任務操作系統,每個運行的任務就是操作系統所做的一件事情,比如你在聽歌的同時還在用MSN和好友聊天。聽歌和聊天就是兩個任務,這個兩個任務是“同時”進行的。一個任務一般對應一個進程,也可能包含好幾個進程。比如運行的MSN就對應一個MSN的進程,如果你用的是windows系統,你就可以在任務管理器中看到操作系統正在運行的進程信息。
一般來說,當運行一個應用程序的時候,就啟動了一個進程,當然有些會啟動多個進程。啟動進程的時候,操作系統會為進程分配資源,其中最主要的資源是內存空間,因為程序是在內存中運行的。在進程中,有些程序流程塊是可以亂序執行的,并且這個代碼塊可以同時被多次執行。實際上,這樣的代碼塊就是線程體。線程是進程中亂序執行的代碼流程。當多個線程同時運行的時候,這樣的執行模式成為并發執行。
多線程的目的是為了最大限度的利用CPU資源。
Java編寫程序都運行在在Java虛擬機(JVM)中,在JVM的內部,程序的多任務是通過線程來實現的。每用java命令啟動一個java應用程序,就會啟動一個JVM進程。在同一個JVM進程中,有且只有一個進程,就是它自己。在這個JVM環境中,所有程序代碼的運行都是以線程來運行。
一般常見的Java應用程序都是單線程的。比如,用java命令運行一個最簡單的HelloWorld的Java應用程序時,就啟動了一個JVM進程,JVM找到程序程序的入口點main(),然后運行main()方法,這樣就產生了一個線程,這個線程稱之為主線程。當main方法結束后,主線程運行完成。JVM進程也隨即退出。
對于一個進程中的多個線程來說,多個線程共享進程的內存塊,當有新的線程產生的時候,操作系統不分配新的內存,而是讓新線程共享原有的進程塊的內存。因此,線程間的通信很容易,速度也很快。不同的進程因為處于不同的內存塊,因此進程之間的通信相對困難。
實際上,操作的系統的多進程實現了多任務并發執行,程序的多線程實現了進程的并發執行。多任務、多進程、多線程的前提都是要求操作系統提供多任務、多進程、多線程的支持。
在Java程序中,JVM負責線程的調度。線程調度是值按照特定的機制為多個線程分配CPU的使用權。調度的模式有兩種:分時調度和搶占式調度。分時調度是所有線程輪流獲得CPU使用權,并平均分配每個線程占用CPU的時間;搶占式調度是根據線程的優先級別來獲取CPU的使用權。JVM的線程調度模式采用了搶占式模式。
所謂的“并發執行”、“同時”其實都不是真正意義上的“同時”。眾所周知,CPU都有個時鐘頻率,表示每秒中能執行cpu指令的次數。在每個時鐘周期內,CPU實際上只能去執行一條(也有可能多條)指令。操作系統將進程線程進行管理,輪流(沒有固定的順序)分配每個進程很短的一段是時間(不一定是均分),然后在每個線程內部,程序代碼自己處理該進程內部線程的時間分配,多個線程之間相互的切換去執行,這個切換時間也是非常短的。因此多任務、多進程、多線程都是操作系統給人的一種宏觀感受,從微觀角度看,程序的運行是異步執行的。
用一句話做總結:雖然操作系統是多線程的,但CPU每一時刻只能做一件事,和人的大腦是一樣的,呵呵。
二、Java與多線程
Java語言的多線程需要操作系統的支持。
Java 虛擬機允許應用程序并發地運行多個執行線程。Java語言提供了多線程編程的擴展點,并給出了功能強大的線程控制API。
在Java中,多線程的實現有兩種方式: 擴展java.lang.Thread類 實現java.lang.Runnable接口
每個線程都有一個優先級,高優先級線程的執行優先于低優先級線程。每個線程都可以或不可以標記為一個守護程序。當某個線程中運行的代碼創建一個新 Thread 對象時,該新線程的初始優先級被設定為創建線程的優先級,并且當且僅當創建線程是守護線程時,新線程才是守護程序。
當 Java 虛擬機啟動時,通常都會有單個非守護線程(它通常會調用某個指定類的 main 方法)。Java 虛擬機會繼續執行線程,直到下列任一情況出現時為止:
調用了 Runtime 類的 exit 方法,并且安全管理器允許退出操作發生。
非守護線程的所有線程都已停止運行,無論是通過從對 run 方法的調用中返回,還是通過拋出一個傳播到 run 方法之外的異常。
三、擴展java.lang.Thread類
/** * File Name: TestMitiThread.java * Created by: IntelliJ IDEA.* Copyright: Copyright(c)2003-2006 * Company: Lavasoft([url]http://lavasoft.blog.51cto.com/[/url])* Author: leizhimin * Modifier: leizhimin * Date Time: 2007-5-17 10:03:12 * Readme: 通過擴展Thread類實現多線程 */ public class TestMitiThread { public static void main(String[] rags){ System.out.println(Thread.currentThread().getName()+ “ 線程運行開始!”);new MitiSay(“A”).start();new MitiSay(“B”).start();System.out.println(Thread.currentThread().getName()+ “ 線程運行結束!”);} }
class MitiSay extends Thread { public MitiSay(String threadName){ super(threadName);}
public void run(){ System.out.println(getName()+ “ 線程運行開始!”);for(int i = 0;i < 10;i++){ System.out.println(i + “ ” + getName());try { sleep((int)Math.random()* 10);} catch(InterruptedException e){ e.printStackTrace();} } System.out.println(getName()+ “ 線程運行結束!”);} }
運行結果:
main 線程運行開始!main 線程運行結束!A 線程運行開始!0 A 1 A B 線程運行開始!2 A 0 B 3 A 4 A 1 B 5 A 6 A 7 A 8 A 9 A A 線程運行結束!2 B 3 B 4 B 5 B 6 B 7 B 8 B 9 B B 線程運行結束!說明:
程序啟動運行main時候,java虛擬機啟動一個進程,主線程main在main()調用時候被創建。隨著調用MitiSay的兩個對象的start方法,另外兩個線程也啟動了,這樣,整個應用就在多線程下運行。
在一個方法中調用Thread.currentThread().getName()方法,可以獲取當前線程的名字。在mian方法中調用該方法,獲取的是主線程的名字。
注意:start()方法的調用后并不是立即執行多線程代碼,而是使得該線程變為可運行態(Runnable),什么時候運行是由操作系統決定的。
從程序運行的結果可以發現,多線程程序是亂序執行。因此,只有亂序執行的代碼才有必要設計為多線程。
Thread.sleep()方法調用目的是不讓當前線程獨自霸占該進程所獲取的CPU資源,以留出一定時間給其他線程執行的機會。
實際上所有的多線程代碼執行順序都是不確定的,每次執行的結果都是隨機的。
四、實現java.lang.Runnable接口
/** * 通過實現 Runnable 接口實現多線程 */ public class TestMitiThread1 implements Runnable {
public static void main(String[] args){ System.out.println(Thread.currentThread().getName()+ “ 線程運行開始!”);TestMitiThread1 test = new TestMitiThread1();Thread thread1 = new Thread(test);Thread thread2 = new Thread(test);thread1.start();thread2.start();System.out.println(Thread.currentThread().getName()+ “ 線程運行結束!”);}
public void run(){ System.out.println(Thread.currentThread().getName()+ “ 線程運行開始!”);for(int i = 0;i < 10;i++){ System.out.println(i + “ ” + Thread.currentThread().getName());try { Thread.sleep((int)Math.random()* 10);} catch(InterruptedException e){ e.printStackTrace();} } System.out.println(Thread.currentThread().getName()+ “ 線程運行結束!”);} }
運行結果:
main 線程運行開始!Thread-0 線程運行開始!main 線程運行結束!0 Thread-0 Thread-1 線程運行開始!0 Thread-1 1 Thread-1 1 Thread-0 2 Thread-0 2 Thread-1 3 Thread-0 3 Thread-1 4 Thread-0 4 Thread-1 5 Thread-0 6 Thread-0 5 Thread-1 7 Thread-0 8 Thread-0 6 Thread-1 9 Thread-0 7 Thread-1 Thread-0 線程運行結束!8 Thread-1 9 Thread-1 Thread-1 線程運行結束!說明:
TestMitiThread1類通過實現Runnable接口,使得該類有了多線程類的特征。run()方法是多線程程序的一個約定。所有的多線程代碼都在run方法里面。Thread類實際上也是實現了Runnable接口的類。
在啟動的多線程的時候,需要先通過Thread類的構造方法Thread(Runnable target)構造出對象,然后調用Thread對象的start()方法來運行多線程代碼。
實際上所有的多線程代碼都是通過運行Thread的start()方法來運行的。因此,不管是擴展Thread類還是實現Runnable接口來實現多線程,最終還是通過Thread的對象的API來控制線程的,熟悉Thread類的API是進行多線程編程的基礎。
五、讀解Thread類API
static int MAX_PRIORITY 線程可以具有的最高優先級。static int MIN_PRIORITY 線程可以具有的最低優先級。static int NORM_PRIORITY 分配給線程的默認優先級。
構造方法摘要
Thread(Runnable target)分配新的 Thread 對象。Thread(String name)分配新的 Thread 對象。
方法摘要
static Thread currentThread()返回對當前正在執行的線程對象的引用。ClassLoader getContextClassLoader()返回該線程的上下文 ClassLoader。long getId()返回該線程的標識符。String getName()返回該線程的名稱。int getPriority()返回線程的優先級。Thread.State getState()返回該線程的狀態。ThreadGroup getThreadGroup()返回該線程所屬的線程組。static boolean holdsLock(Object obj)當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true。void interrupt()中斷線程。
static boolean interrupted()測試當前線程是否已經中斷。boolean isAlive()測試線程是否處于活動狀態。boolean isDaemon()測試該線程是否為守護線程。boolean isInterrupted()測試線程是否已經中斷。void join()等待該線程終止。void join(long millis)等待該線程終止的時間最長為 millis 毫秒。void join(long millis, int nanos)等待該線程終止的時間最長為 millis 毫秒 + nanos 納秒。void resume()已過時。該方法只與 suspend()一起使用,但 suspend()已經遭到反對,因為它具有死鎖傾向。有關更多信息,請參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對?。void run()如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;否則,該方法不執行任何操作并返回。void setContextClassLoader(ClassLoader cl)設置該線程的上下文 ClassLoader。void setDaemon(boolean on)將該線程標記為守護線程或用戶線程。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)設置當線程由于未捕獲到異常而突然終止,并且沒有為該線程定義其他處理程序時所調用的默認處理程序。void setName(String name)改變線程名稱,使之與參數 name 相同。void setPriority(int newPriority)更改線程的優先級。
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)設置該線程由于未捕獲到異常而突然終止時調用的處理程序。static void sleep(long millis)在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)。static void sleep(long millis, int nanos)在指定的毫秒數加指定的納秒數內讓當前正在執行的線程休眠(暫停執行)。void start()使該線程開始執行;Java 虛擬機調用該線程的 run 方法。void stop()已過時。該方法具有固有的不安全性。用 Thread.stop 來終止線程將釋放它已經鎖定的所有監視器(作為沿堆棧向上傳播的未檢查 ThreadDeath 異常的一個自然后果)。如果以前受這些監視器保護的任何對象都處于一種不一致的狀態,則損壞的對象將對其他線程可見,這有可能導致任意的行為。stop 的許多使用都應由只修改某些變量以指示目標線程應該停止運行的代碼來取代。目標線程應定期檢查該變量,并且如果該變量指示它要停止運行,則從其運行方法依次返回。如果目標線程等待很長時間(例如基于一個條件變量),則應使用 interrupt 方法來中斷該等待。有關更多信息,請參閱《為何不贊成使用 Thread.stop、Thread.suspend 和 Thread.resume?》。void stop(Throwable obj)已過時。該方法具有固有的不安全性。請參閱 stop()以獲得詳細信息。該方法的附加危險是它可用于生成目標線程未準備處理的異常(包括若沒有該方法該線程不太可能拋出的已檢查的異常)。有關更多信息,請參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對?。void suspend()已過時。該方法已經遭到反對,因為它具有固有的死鎖傾向。如果目標線程掛起時在保護關鍵系統資源的監視器上保持有鎖,則在目標線程重新開始以前任何線程都不能訪問該資源。如果重新開始目標線程的線程想在調用 resume 之前鎖定該監視器,則會發生死鎖。這類死鎖通常會證明自己是“凍結”的進程。有關更多信息,請參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對?。String toString()返回該線程的字符串表示形式,包括線程名稱、優先級和線程組。static void yield()暫停當前正在執行的線程對象,并執行其他線程。
六、線程的狀態轉換圖
線程在一定條件下,狀態會發生變化。線程變化的狀態轉換圖如下:
1、新建狀態(New):新創建了一個線程對象。
2、就緒狀態(Runnable):線程對象創建后,其他線程調用了該對象的start()方法。該狀態的線程位于可運行線程池中,變得可運行,等待獲取CPU的使用權。
3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
(一)、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。
(二)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入鎖池中。
(三)、其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
七、線程的調度
1、調整線程優先級:Java線程有優先級,優先級高的線程會獲得較多的運行機會。
Java線程的優先級用整數表示,取值范圍是1~10,Thread類有以下三個靜態常量: static int MAX_PRIORITY 線程可以具有的最高優先級,取值為10。static int MIN_PRIORITY 線程可以具有的最低優先級,取值為1。static int NORM_PRIORITY 分配給線程的默認優先級,取值為5。
Thread類的setPriority()和getPriority()方法分別用來設置和獲取線程的優先級。
每個線程都有默認的優先級。主線程的默認優先級為Thread.NORM_PRIORITY。
線程的優先級有繼承關系,比如A線程中創建了B線程,那么B將和A具有相同的優先級。JVM提供了10個線程優先級,但與常見的操作系統都不能很好的映射。如果希望程序能移植到各個操作系統中,應該僅僅使用Thread類有以下三個靜態常量作為優先級,這樣能保證同樣的優先級采用了同樣的調度方式。
2、線程睡眠:Thread.sleep(long millis)方法,使線程轉到阻塞狀態。millis參數設定睡眠的時間,以毫秒為單位。當睡眠結束后,就轉為就緒(Runnable)狀態。sleep()平臺移植性好。
3、線程等待:Object類中的wait()方法,導致當前的線程等待,直到其他線程調用此對象的 notify()方法或 notifyAll()喚醒方法。這個兩個喚醒方法也是Object類中的方法,行為等價于調用 wait(0)一樣。
4、線程讓步:Thread.yield()方法,暫停當前正在執行的線程對象,把執行機會讓給相同或者更高優先級的線程。
5、線程加入:join()方法,等待其他線程終止。在當前線程中調用另一個線程的join()方法,則當前線程轉入阻塞狀態,直到另一個進程運行結束,當前線程再由阻塞轉為就緒狀態。
6、線程喚醒:Object類中的notify()方法,喚醒在此對象監視器上等待的單個線程。如果所有線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,并在對實現做出決定時發生。線程通過調用其中一個 wait 方法,在對象的監視器上等待。直到當前的線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其他所有線程進行競爭;例如,喚醒的線程在作為鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。類似的方法還有一個notifyAll(),喚醒在此對象監視器上等待的所有線程。注意:Thread中suspend()和resume()兩個方法在JDK1.5中已經廢除,不再介紹。因為有死鎖傾向。
7、常見線程名詞解釋
主線程:JVM調用程序mian()所產生的線程。
當前線程:這個是容易混淆的概念。一般指通過Thread.currentThread()來獲取的進程。后臺線程:指為其他線程提供服務的線程,也稱為守護線程。JVM的垃圾回收線程就是一個后臺線程。
前臺線程:是指接受后臺線程服務的線程,其實前臺后臺線程是聯系在一起,就像傀儡和幕后操縱者一樣的關系。傀儡是前臺線程、幕后操縱者是后臺線程。由前臺線程創建的線程默認也是前臺線程。可以通過isDaemon()和setDaemon()方法來判斷和設置一個線程是否為后臺線程。
本文出自 “熔 巖” 博客,請務必保留此出處http://lavasoft.blog.51cto.com/62575/27069
第三篇:JAVA基礎總結
JAVA基礎總結
轉眼間,已經來到這里學習半年了,而現在我們對于JAVA學習才算是真正的開始。一開始接觸的時候我發現這個和C語言的基本語法幾乎一模一樣的,老師說:JAVA語言本來就是C++語言發展過來的,而C++是從C語言發展來的,C C++ 還有JAVA雖然是不同的三種語言,但是其實他們的基本語法是一樣的,但是它們卻有巨大的區別,這個區別主要是體現在思想上。
都說C語言是面向過程的語言,C++ C#和JAVA是面向對象的編程,其實就是思維方式稍微改了一下子,面向過程的語言主要是著重于算法,面向對象著重于邏輯而已。
這個教我們的老師是張成峰老師,張成峰是個很負責的老師,雖然JAVA基礎其實和C語言基礎差不多,但是仔細學學還是能找出很多不同的細節的,于是大家的問題就很多,張老師幾乎就是手把手的教我們,所以整體來說JAVA基礎學得挺扎實的。
我在這本書的學習不是挺好,聯系得少了,所以對代碼也不是特別熟悉。而且JAVA是一門重概念的書,對于我來說,概念是我的要害,理論知識也是我最怕學習的,所以學習這本書對我來說很是艱難,聽了很久的課,代碼寫得出來,但是理論知識幾乎一點也不明白,結果就造成了這次筆試考的不好。
筆試考的不好,機試也因為我的粗心沒有考好,所以這次的成績不好。
學習JAVA基礎后,還要在學習的過程中對每個知識都要用心和細心,當然最該注重的地方就是應該提高我對理論知識的學習,要多看書,也要多敲敲代碼,這些就是提高學習知識全面的方法。
下一本書學習的是JAVAOO算是JAVA的一個重要的內容了,這才會真正的接觸JAVA的核心課程,在這本書里我要好好的理解理論知識,仔細的學習每個知識。
第四篇:Java基礎總結
? 基本數據類型
? 變量單元直接存放數據
? 賦值和傳參的方式:傳值,即傳遞副本
? 比較相等使用關系運算符“==”
? 引用數據類型
? 變量單元存放引用即對象地址,而數據(對象)在另一內存區域存放。
? 賦值和傳參的方式:傳引用(傳遞對象的地址),即傳遞對象本身。
? 使用關系運算符“==”比較兩個引用類型變量,比較的是地址,如果比較結果為
真,說明兩個變量引用了同一對象。
? 比較兩個對象是否相等應使用equals方法
? Java是面向對象的語言,開發Java程序就是開發Java類,Java的變量定義、方法(函
數)和語句都必須在類中書寫,“無類即無Java”。
? 類的實例成員
屬性(成員變量):直接定義在類體中的變量(注:在方法或語句塊中定義的變量不是屬性)
方法:直接定義在類體中的方法。
類成員:直接定義在類體中的內部類,即類成員。
注:對象的成員是從屬于對象的,必須通過對象訪問,在Java中不存在脫離對象和類而獨立存在的屬性和方法。
? 類、方法、語句三者的關系:
類中包含方法,方法中包含語句;方法中不能嵌套方法;語句不能直接寫在類體中。
? 變量的作用域
? 局部變量-方法的參數、方法體中定義的變量、語句塊中定義的變量,僅在所定義的方法體或語句塊中有效。
? 屬性-在整個類體中有效。
? 公有的屬性-其它類可以本類對象訪問。
? 私有的屬性-僅限本類內訪問
? 局部變量(無論基本類型還是引用類型)在棧內存中,對象在堆內存中。注:引用類型的局部變量內存放是對象的引用(即地址),而對象在堆內存中。
? 方法的重載-在同一類中多個方法同名的語法現象,方法重載應符合以下條件:
? 方法同名
? 參數有所不同(即類型、個數和順序三者至少有一個不同)
注:方法是否重載不考慮返回類型等其它方面。
? 包和訪問修飾符
? 包-類的目錄結構,主要用途是方便類的管理。
? 類的簡稱:不含包名的類名
? 類的全限定名稱(類的全稱):帶包名的類名。
? 訪問修飾符
? private-僅限本類內訪問
? public-任何類都能夠訪問
? 默認-限本包的類訪問
? protected--限本包的類訪問和子類的訪問
? 類的成員(屬性、方法和成員內部類)可以使用四種訪問修飾符,頂級外部類僅能
使用public和默認兩種修飾符。
? 數組
? 基本類型的數組的元素放的是數據
? 對象數據的元素放的是對象的引用
? 二維數組實際上是一個維數組,而其每個元素又是一個一維數組。
? 構造方法-與類名同名并且無返回類型的方法
? 構造方法的作用是創建對象,僅能通過new關鍵字調用。
? 類中未顯式定義構造方法時,類中會有默認的構造方法(即一個public、無參的構
造方法);類中一旦定義顯式定義構造方法,將不再產生默認的構造方法。
? 構造方法可以重載
? 構造方法只能可以使用四個訪問修飾符,不可以使用其它修飾符(如static、final
等)。
? this關鍵字
? this表示本對象或對象自身的引用
? 通過this可以調用本類的成員方法和屬性
? 通過this可以調用本類的構造方法,this調用構造方法的語句必須寫在構造方法的第一句。
? 實例成員和靜態成員
? 類的成員(屬性、方法和成員類)可劃分為靜態成員和實例成員。
? 實例成員是屬于對象的,隨著對象的創建而存在,隨著對象的銷毀而銷毀。? 靜態成員是屬于類的,隨著類的加載而存在,隨著類的銷毀而銷毀。
? 使用static修飾的成員是靜態成員,未使用static修飾的成員是實例成員。? 靜態成員內不能使用this關鍵字。
? this表示當前對象的引用。
? 對象的初始化
? 第一:實例屬性賦默認值
? 第二:實例屬性賦初值
? 第三:執行對象初始化塊
? 第四:執行構造方法
? 類的初始化
? 第一:靜態屬性賦默認值
? 第二:靜態屬性賦初值
? 第三:執行靜態塊
? 繼承的基本概念
? Java僅支持單繼承
?
?
?
?
? ? 如果一個類沒有顯式繼承任何類,則隱式繼承java.lang.Object類 ? 子類擁有父類的一切,子類對象由兩部分構成:父類對象部分和子類個性化的部分。? 子類并不能訪問父類的一切: ? 子類可以訪問父類的public成員和protected成員 ? 子類不可以訪問父類的private成員 ? 僅當子類與父類同在一包時,子類方可以訪問父類的默認訪問控制的成員。繼承與類和對象的初始化 ? 構造子類對象時,首先構造父類對象,其次構造子類個性化部分,兩者共同構成完整的子類對象,即首先進行父類對象的初始化,在初始化子類對象(個性化)部分。? 子類構造方法的執行首先調用父類的構造方法。? 若在子類構造方法中無顯式調用父類構造方法的語句,則系統默認調用父類中可訪問的無參的構造方法,如果這時候父類中恰好沒有這樣的構造方法,則編譯出錯。? 在子類的構造方法中可以通過super關鍵字調用父類構造方法。這樣的調用語句只能出現在子類構造方法的第一句。? 關于初始化的順序 ? 初始化父類 ? 初始化子類 ? 初始化父類對象 ? 初始化子類對象 繼承與類型轉換 ? 子類型對象可以自動轉型為父類型 ? 父類型引用某個子類型對象時,可以強制轉化為這個具體的子類型 方法重寫 ? 在繼承的情況下,子類的實例方法與父類的實例方法的方法名稱、參數、返回類型、throws聲明完全一致,并且該子類方法的訪問權限不低于父類方法的訪問權限,即方法重寫(子類方法重寫了父類方法),也稱方法覆蓋。? 方法重寫僅存在于父子類中的實例方法,靜態方法沒有重寫的概念。? 當通過子類型對象執行重寫方法時,將始終表現為子類的行為,而且無論引用對象的變量是父類型還是子類型,也無論是直接調用還是通過父類型其它方法間接調用,都將如此。? final修飾的方法不可以被重寫 ? final修飾的類不可以被繼承 隱藏成員變量 ? 如果子類和父類中定義了同名稱的成員變量,則稱子類隱藏了父類的成員變量 ? 通過父類方法訪問隱藏的成員變量時,將獲得父類成員變量 ? 通過子類方法訪問隱藏的成員變量時,將獲得子類成員變量 ? 通過父類型的引用直接訪問隱藏的成員變量時,將獲得父類成員變量 ? 通過子類型的引用直接訪問隱藏的成員變量時,將獲得子類成員變量 super關鍵字
? super僅能用于子類中表示本對象的父對象部分
? super可以調用父類型的構造方法
? Super可以調用父類的成員
? Super不可以使用在靜態上下文中
? Super不可以做為參數傳遞,不可以做為返回值返回。
? 當方法重寫時,或父類成員變量被隱藏時,子類中只能通過super訪問父類方法和
父類成員變量
? final關鍵字
? 用于變量,表示變量的值不可改變
? 用于類,表示類不可被繼承
? 用于方法,表示方法不可以被重寫
? 關于final變量的初始化時機
? 局部變量:聲明的時候初始化
? 實例成員變量:聲明、對象初始化塊和構造方法三處之一
? 靜態成員變量:聲明、靜態塊兩處之一
? 抽象類
? 抽象類可以包含抽象方法,也可以不包含抽象方法
? 含有抽象方法的類必須定義為抽象類
? 抽象類有構造方法
? 抽象類不能夠實例化
? 通過抽象類可以調用其靜態成員
? 抽象類是需要由子類繼承的,因此抽象類不允許是final類
? 抽象方法
? 抽象方法沒有方法體,包括一對空的大括號也不允許有
? 抽象方法必須是實例方法,抽象方法不允許是final的? 抽象類與繼承
? 抽象類可以被繼承
? 若抽象類的子類是非抽象類,則該子類必須實現(重寫)其父類的所有抽象方法 ? 若抽象類的子類也是抽象類,則該子類可以不實現(重寫)其父類的全部或部分抽象
方法。
? 接口
? 接口也是數據類型,可以將其理解為“純”抽象類
? 接口不是類,也沒有構造方法,不能夠實例化
? 接口中的屬性一律是public、static、final的,并可以省略這三個關鍵字
? 接口的方法一律是public、abstract的,并且可以省略這兩個關鍵字
? 接口中可以不包含任何屬性和方法
? 接口與實現
? 接口不是類,因此接口與類的關系不是“繼承”關系,而是“實現”關系,我們可
以將實現理解為繼承(盡管這并不恰當)
? 如果接口的實現類是抽象類,則該實現類可以不實現接口的全部或部分方法 ? 如果接口的實現類是非抽象類,則該實現類必須實現接口的全部方法
? 一個類可以實現多個接口
? 接口與繼承
? 接口之間可以相互繼承
? 一個接口可以繼承多個接口
? 接口與類型轉換
? 接口的的子類型對象可以自動向上轉型為接口類型
? 接口的子類型指:是接口的實現類或者接口的子接口
? 如果變量引用的對象實際是某個接口的實現類對象,而變量的類型不是這個接口的子類型,那么則可以強制轉換為這個接口類型。
? 異常的類層次結構
? Throwable錯誤類,表示不可恢復的致命錯誤
? Exception運行時異常,此類異常可以不做顯式處理
? 非運行時異常catch
? 聲明拋出 在方法頭通過throws聲明可能拋出的異常類型
? 異常機制的五個關鍵字
? try catch finally throw throws
? 如何使用
? try-catch
? try-catch-finally注:只要try執行,其對應的finally塊才必然執行
? try-finally注:只要try執行,其對應的finally塊才必然執行
? throw 主動拋出一個異常
? throws 用在方法聲明頭部,聲明方法可能拋出異常
? finally代碼塊多用于書寫資源回收代碼
? Java集合類(集合框架)
? Collection接口
? List接口 允許重復元素,元素有索引序號,并按放入元素的次序編號
? ArrayList 線性表結構,查找快,增刪慢
? LinkedList 鏈表結構,查找慢,增刪快
? Vector 同步,查找、增刪性能都不高。
? Set接口 不允許重復元素,元素無索引編號
? HashSet 元素散列存放
? TreeSet元素按自然順序排序(即從小到大排序)
? Map接口
? HashMap
? 允許null值和null鍵
? 不同步
? Hashtable <--Properties
? 不允許null值和null鍵
? 同步
? 內部類
第五篇:多核多線程題目總結
2.并行和并發的概念與區別:
-如果某個系統支持兩個或多個動作(Action)同時存在,那么這個系統就是一個并發系統-如果某個系統支持兩個或多個動作同時執行,那么這個系統就是一個并行系統
-并發程序可同時擁有兩個或多個線程。如果程序能夠并行執行,則一定是運行在多核處理器上,每個線程都將分配到一個獨立的處理器核上。
-“并行”概念是“并發”概念的一個子集 3.并行計算技術的主要目的:
加速求解問題的速度
例如,給定某應用,在單處理器上,串行執行需要2 周,這個速度對一般的應用而言,是無法忍受的。于是,可以借助并行計算,使用100 臺處理器,加速50 倍,將執行時間縮短為6.72 個小時。
提高求解問題的規模
例如,在單處理器上,受內存資源2GB的限制,只能計算10 萬個網格,但是,當前數值模擬要求計算千萬個網格。于是,也可以借助并行計算,使用100 個處理器,將問題求解規模線性地擴大100 倍。并行計算的主要目標:
在并行機上,解決具有重大挑戰性計算任務的科學、工程及商業計算問題,滿足不斷增長的應用問題對速度和內存資源的需求。
4.并行計算的主要研究內容大致可分為四個方面:
并行機的高性能特征抽取
充分理解和抽取當前并行機體系結構的高性能特征,提出實用的并行計算模型和并行性能評價方法,指導并行算法的設計和并行程序的實現。
并行算法設計與分析
設計高效率的并行算法,將應用問題分解為可并行計算的多個子任務,并具體分析這些算法的可行性和效果。
并行實現技術
主要包含并行程序設計和并行性能優化。
并行應用
這是并行計算研究的最終目的。通過驗證和確認并行程序的正確性和效率,進一步將程序發展為并行應用軟件,應用于求解實際問題。同時,結合實際應用出現的各種問題,不斷地改進并行算法和并行程序。5.并行程序執行時間
對各個進程,墻上時間可進一步分解為計算CPU時間、通信CPU時間、同步開銷時間、同步導致的進程空閑時間
計算CPU時間:進程指令執行所花費的CPU時間,包括程序本身的指令執行占用的時間(用戶時間)和系統指令花費的時間;
通信CPU時間:進程通信花費的CPU時間; 同步開銷時間:進程同步花費的時間;
進程空閑時間:進程空閑時間是指并行程序執行過程中,進程所有空閑時間總和(如進程阻塞式等待其他進程的消息時。此時CPU通常是空閑的,或者處于等待狀態)6.并行程序性能優化
最主要的是選擇好的并行算法和通信模式
減少通信量、提高通信粒度
提高通信粒度的有效方法就是減少通信次數,盡可能將可以一次傳遞的數據合并起來一起傳遞
全局通信盡量利用高效集合通信算法
對于標準的集合通信,如廣播、規約、數據散發與收集等,盡量調用MPI標準庫函數 挖掘算法的并行度,減少CPU空閑等待
具有數據相關性的計算過程會導致并行運行的部分進程空閑等待.在這種情況下,可以考慮改變算法來消除數據相關性
7.順序程序的特性
順序性:處理機嚴格按照指令次序依次執行,即僅當一條指令執行完后才開始執行下一條指令;
封閉性:程序在執行過程中獨占系統中的全部資源,該程序的運行環境只與其自身動作有關,不受其它程序及外界因素影響;
可再現性:程序的執行結果與執行速度無關,而只與初始條件有關,給定相同的初始條件,程序的任意多次執行一定得到相同的執行結果.8.并發程序特性
交叉性:程序并發執行對應某一種交叉,不同的交叉可能導致不同的計算結果,操作系統應當保證只產生導致正確結果的交叉,去除那些可能導致不正確結果的交叉;
非封閉性:一個進程的運行環境可能被其它進程所改變,從而相互影響;
不可再現性:由于交叉的隨機性,并發程序的多次執行可能對應不同的交叉,因而不能期望重新運行的程序能夠再現上次運行的結果。
10.Win32線程同步的實現
11.數據作用域對數據值的影響
12.分析程序的結果
13.并行區域編程與parallel for語句的區別
-并行區域采用了復制執行方式,將代碼在所有的線程內各執行一次;
-循環并行化則是采用工作分配執行方式,將循環需做的所有工作量,按一定的方式分配給各個執行線程,全部線程執行工作的總合等于原先串行執行所完成的工作量。14.OpenMP提供三種不同的互斥鎖機制: 臨界區(critical)原子操作(atomic)由庫函數來提供同步操作 int counter=0;#pragma omp parallel {
for(int i=0;i<10000;i++)
#pragma omp atomic //atomic operation
}
printf(“counter = %dn”,counter);
counter=20000 25.影響性能的主要因素
并行化代碼在應用程序中的比率-OpenMP 本身的開銷
-OpenMP 獲得應用程序多線程并行化的能力,需要一定的程序庫支持。在這些庫程序對程序并行加速的同時也需要運行庫本身-負載均衡
-局部性
在程序運行過程中,高速緩存將緩存最近剛剛訪問過的數據及其相鄰的數據。因此,在編寫程序的時候,需要考慮到高速緩存的作用,有意地運用這種局部性帶來的高速緩存的效率提高。-線程同步帶來的開銷
多個線程在進行同步的時候必然帶來一定的同步開銷,在使用多線程進行開發時需要考慮同步的必要性,消除不必要的同步,或者調整同步的順序,就有可能帶來性能上的提升。26.什么是MPI 消息傳遞接口(Message Passing Interface,簡稱MPI)是一種編程接口標準,而不是一種具體的編程語言。基本含義:MPI標準定義了一組具有可移植性的編程接口。
特征:
1、典型的實現包括開源的MPICH、LAM MPI以及不開源的INTEL MPI。
2、程序員設計好應用程序并行算法,調用這些接口,鏈接相應平臺上的MPI庫,即可實現基于消息傳遞的并行計算
27.MPICH的安裝和配置 counter++;
28.MPI程序的四個基本函數-MPI_Init和MPI_Finalize MPI_Init初始化MPI執行環境,建立多個MPI進程之間的聯系,為后續通信做準備。而MPI_Finalize則是結束MPI執行環境。這兩個函數用來定義MPI程序的并行區-MPI_Comm_rank
用來標識各個MPI進程,兩個函數參數:
MPI_Comm類型的通信域,表示參與計算的MPI進程組 &rank,返回調用進程在comm中的標識號-MPI_Comm_size 用來標識相應進程組中有多少個進程,有兩個參數: MPI_Comm類型的通信域,標尺參與計算的MPI進程組 整型指針,返回相應進程組中的進程數
29.MPI的點對點通信
兩個最重要的MPI函數MPI_Send和MPI_Recv。
-int MPI_SEND(buf, count, datatype, dest, tag, comm)這個函數的含義是向通信域comm中的dest進程發送數據。消息數據存放在buf中,類型是datatype,個數是count個。這個消息的標志是tag,用以和本進程向同一目的進程發送的其他消息區別開來。
-int MPI_RECV(buf,count,datatype,source,tag,comm,status)MPI_Recv絕大多數的參數和MPI_Send相對應,有相同的意義。唯一的區別就是MPI_Recv里面多了一個參數status。status主要顯示接收函數的各種錯誤狀態。30.消息管理7要素 發送或者接收緩沖區buf; 數據數量count; 數據類型datatype;
目標進程或者源進程destination/source; 消息標簽tag; 通信域comm;.消息狀態status,只在接收的函數中出現。31.MPI群集通信
群集通信是包含了一對多、多對一和多對多的進程通信模式。其最大特點是多個進程參與通信。常用的MPI群集通信函數: 同步
廣播 聚集 播撒