java多线程编程--基础篇
一、基本概念
a、操作系統(tǒng)中進(jìn)程與線程的概念
現(xiàn)在的操作系統(tǒng)是多任務(wù)操作系統(tǒng)。多線程是實(shí)現(xiàn)多任務(wù)的一種方式。 進(jìn)程是指一個(gè)內(nèi)存中運(yùn)行的應(yīng)用程序,每個(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間,
一個(gè)進(jìn)程中可以啟動(dòng)多個(gè)線程。比如在Windows系統(tǒng)中,一個(gè)運(yùn)行的exe就是一個(gè)進(jìn)程。
線程是指進(jìn)程中的一個(gè)執(zhí)行流程,一個(gè)進(jìn)程中可以運(yùn)行多個(gè)線程。比如java.exe進(jìn)程中可以運(yùn)行很多線程。線程總是屬于某個(gè)進(jìn)程,進(jìn)程中的多個(gè)線程共享進(jìn)程的內(nèi)存空間。
“同時(shí)”執(zhí)行是人的感覺,在線程之間實(shí)際上輪換執(zhí)行。
b、Java中的線程 在Java中,“線程”指兩件不同的事情: ??????
?????????? a、java.lang.Thread類的一個(gè)實(shí)例。 ??????
???????????b、線程的執(zhí)行。
使用java.lang.Thread類或者java.lang.Runnable接口編寫代碼來定義、實(shí)例化和啟動(dòng)新線程。
一個(gè)Thread類實(shí)例只是一個(gè)對象,像Java中的任何其他對象一樣,具有變量和方法,在堆上完成對象的創(chuàng)建和銷毀。
Java中,每個(gè)線程都有一個(gè)調(diào)用棧,即使不在程序中創(chuàng)建任何新的線程,線程也在后臺(tái)運(yùn)行著。
一個(gè)Java應(yīng)用總是從main()方法開始運(yùn)行,mian()方法運(yùn)行在一個(gè)線程內(nèi),它被稱為主線程。
一旦創(chuàng)建一個(gè)新的線程,就產(chǎn)生一個(gè)新的調(diào)用棧。線程總體分兩類:用戶線程和守護(hù)線程。
當(dāng)所有用戶線程執(zhí)行完畢的時(shí)候,JVM自動(dòng)關(guān)閉。但是守護(hù)線程卻獨(dú)立于JVM,守護(hù)線程一般是由操作系統(tǒng)或者用戶自己創(chuàng)建的。
二、線程的創(chuàng)建與啟動(dòng)
a、定義線程
1、繼承java.lang.Thread類。
此類中有個(gè)run()方法,應(yīng)該注意其用法: public void run() 如果該線程是使用獨(dú)立的 Runnable 運(yùn)行對象構(gòu)造的,則調(diào)用該 Runnable 對象的 run 方法;否則,該方法不執(zhí)行任何操作并返回。??Thread 的子類應(yīng)該重寫該方法。
2、實(shí)現(xiàn)java.lang.Runnable接口。
void run() 使用實(shí)現(xiàn)接口 Runnable 的對象創(chuàng)建一個(gè)線程時(shí),啟動(dòng)該線程將導(dǎo)致在獨(dú)立執(zhí)行的線程中調(diào)用對象的 run 方法。??
方法 run 的常規(guī)協(xié)定是,它可能執(zhí)行任何所需的操作。
b、實(shí)例化線程
1、如果是繼承java.lang.Thread類的線程,則直接new即可。
即使用的時(shí)候Thread a=new tThread();2、如果是實(shí)現(xiàn)了java.lang.Runnable接口的類,則用Thread的構(gòu)造方法:
Thread(Runnable target) Thread(Runnable target, String name) Thread(ThreadGroup group, Runnable target) Thread(ThreadGroup group, Runnable target, String name) Thread(ThreadGroup group, Runnable target, String name, long stackSize)?
rThread a=new rThread();//此處可以使用各個(gè)構(gòu)造函數(shù) Thread b=new Thread(a);?
c、啟動(dòng)線程
??????? 在線程的Thread對象上調(diào)用start()方法,而不是run()或者別的方法。 在調(diào)用start()方法之前:線程處于新狀態(tài)中,新狀態(tài)指有一個(gè)Thread對象,但還沒有一個(gè)真正的線程。 在調(diào)用start()方法之后,發(fā)生了一系列復(fù)雜的事情:啟動(dòng)新的執(zhí)行線程(具有新的調(diào)用棧); 該線程從新狀態(tài)轉(zhuǎn)移到可運(yùn)行狀態(tài); 當(dāng)該線程獲得機(jī)會(huì)執(zhí)行時(shí),其目標(biāo)run()方法將運(yùn)行。
start()方法后,run()方法并沒有執(zhí)行。
注意:對Java來說,run()方法沒有任何特別之處。像main()方法一樣,它只是新線程知道調(diào)用的方法名稱(和簽名)。因此,在Runnable上或者Thread上調(diào)用run方法是合法的,但并不啟動(dòng)新的線程,所以通常不使用
d、基本例子
?1、實(shí)現(xiàn)Runnable接口的多線程例子
package com.yunhe.thread; /*** 實(shí)現(xiàn)Runable接口的類* */ public class DoSomething implements Runnable {private String name;public DoSomething(String name) {this.name = name;}public void run() {for (int i = 0; i < 5; i++) {for (long k = 0; k < 100000000; k++);System.out.println(name + ": " + i);}} }測試類代碼:
package com.yunhe.thread; /*** 實(shí)現(xiàn)Runnable的線程的測試類*/public class TestRunnable {public static void main(String[] args) {DoSomething ds1=new DoSomething("tianti");DoSomething ds2=new DoSomething("yunhe");Thread t1=new Thread(ds1);Thread t2=new Thread(ds2);t1.start();t2.start();}}
運(yùn)行結(jié)果:
tianti: 0 yunhe: 0 yunhe: 1 tianti: 1 yunhe: 2 tianti: 2 yunhe: 3 tianti: 3 yunhe: 4 tianti: 42、繼承Thread實(shí)現(xiàn)線程的例子
package com.yunhe.thread; /*** 繼承Thread的線程類* */ public class DoOtherthing extends Thread {public DoOtherthing(String name) {super(name);}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();for (int i = 0; i < 5; i++) {for (long k = 0; k < 1000000000; k++);System.out.println(this.getName() + ": " + i);}}}測試類代碼:
package com.yunhe.thread; /*** 繼承Thread類* */ public class DoOtherthing extends Thread {public DoOtherthing(String name) {super(name);}@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();for (int i = 0; i < 5; i++) {for (long k = 0; k < 1000000000; k++);System.out.println(this.getName() + ": " + i);}}}運(yùn)行結(jié)果:
tianti: 0 yunhe: 0 tianti: 1 yunhe: 1 tianti: 2 yunhe: 2 tianti: 3 yunhe: 3 tianti: 4 yunhe: 4對于上面的多線程程序代碼來說,輸出的結(jié)果是不確定的。
其中for (long k = 0; k < 100000000; k++);是模擬非常耗時(shí)的操作,k值上限較小的時(shí)候輸出是順序的,較大的時(shí)候是不穩(wěn)定的交叉輸出。
e、一些常見的問題
1、線程的名字,一個(gè)運(yùn)行中的線程總是有名字的,名字有兩個(gè)來源,一個(gè)是JVM虛擬機(jī)自己給的名字,一個(gè)是你自己的定的名字。在沒有指定線程名字的情況下,JVM虛擬機(jī)總會(huì)為線程指定名字,并且主線程的名字總是main,非主線程的名字不確定。
2、線程都可以設(shè)置名字,也可以獲取線程的名字,連主線程也不例外。
3、獲取當(dāng)前線程的對象的方法是:Thread.currentThread();
4、在上面的代碼中,只能保證:每個(gè)線程都將啟動(dòng),每個(gè)線程都將運(yùn)行直到完成。一系列線程以某種順序啟動(dòng)并不意味著將按該順序執(zhí)行。對于任何一組啟動(dòng)的線程來說,調(diào)度程序不能保證其執(zhí)行次序,持續(xù)時(shí)間也無法保證。
5、當(dāng)線程目標(biāo)run()方法結(jié)束時(shí)該線程完成。
6、一旦線程啟動(dòng),它就永遠(yuǎn)不能再重新啟動(dòng)。只有當(dāng)線程是新的線程的時(shí)候才可以被啟動(dòng),并且只能一次。一個(gè)可運(yùn)行的線程(就緒狀態(tài))或死線程可以被重新啟動(dòng)。
7、線程的調(diào)度是JVM的一部分,在一個(gè)CPU的機(jī)器上上,實(shí)際上一次只能運(yùn)行一個(gè)線程。一次只有一個(gè)線程棧執(zhí)行。JVM線程調(diào)度程序決定實(shí)際運(yùn)行哪個(gè)處于可運(yùn)行狀態(tài)的線程(就緒狀態(tài))。
眾多可運(yùn)行線程中的某一個(gè)會(huì)被選中做為當(dāng)前線程。可運(yùn)行線程被選擇運(yùn)行的順序是沒有保障的。
8、盡管通常采用隊(duì)列形式,但這是沒有保障的。隊(duì)列形式是指當(dāng)一個(gè)線程完成“一輪”時(shí),它移到可運(yùn)行隊(duì)列的尾部等待,直到它最終排隊(duì)到該隊(duì)列的前端為止,它才能被再次選中。事實(shí)上,我們把它稱為可運(yùn)行池而不是一個(gè)可運(yùn)行隊(duì)列,目的是幫助認(rèn)識(shí)線程運(yùn)行并不都是以某種有保障的順序排列來調(diào)度線程。
9、盡管我們不能控制線程調(diào)度的順序,但可以通過別的方式來影響線程調(diào)度的方式。
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/shudonghe/p/3276387.html
與50位技術(shù)專家面對面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的java多线程编程--基础篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [置顶] ProDinner体验
- 下一篇: 【PAT】1009. Product o