總結(jié)是把一定階段內(nèi)的有關(guān)情況分析研究,做出有指導(dǎo)性的經(jīng)驗(yàn)方法以及結(jié)論的書(shū)面材料,它可以使我們更有效率,不妨坐下來(lái)好好寫(xiě)寫(xiě)總結(jié)吧。寫(xiě)總結(jié)的時(shí)候需要注意什么呢?有哪些格式需要注意呢?下面是小編整理的個(gè)人今后的總結(jié)范文,歡迎閱讀分享,希望對(duì)大家有所幫助。
java多線程知識(shí)點(diǎn)總結(jié)篇一
轉(zhuǎn)載▼
標(biāo)簽:
it
c++本身并沒(méi)有提供任何多線程機(jī)制,但是在windows下,我們可以調(diào)用sdk win32 api來(lái)編寫(xiě)多線程的程序,下面就此簡(jiǎn)單的講一下:
創(chuàng)建線程的函數(shù)
handle createthread(lpsecurity_attributes lpthreadattributes, // sd
size_t dwstacksize,// initial stack size
lpthread_start_routine lpstartaddress,// thread function
lpvoid lpparameter,// thread argument
dword dwcreationflags,// creation option
lpdword lpthreadid// thread identifier);
在這里我們只用到了第三個(gè)和第四個(gè)參數(shù),第三個(gè)參數(shù)傳遞了一個(gè)函數(shù)的地址,也是我們要指定的新的線程,第四個(gè)參數(shù)是傳給新線程的參數(shù)指針。
eg1:
#include
#include using namespace std;dword winapi fun(lpvoid lpparamter)
{
while(1){ cout<<“fun display!”<
}
int main()
{
handle hthread = createthread(null, 0, fun, null, 0, null);
closehandle(hthread);
while(1){ cout<<“main display!”<
return 0;
}
我們可以看到主線程(main函數(shù))和我們自己的線程(fun函數(shù))是隨機(jī)地交替執(zhí)行的,但是兩個(gè)線程輸出太快,使我們很難看清楚,我們可以使用函數(shù)
void sleep(dword dwmilliseconds// sleep time);
來(lái)暫停線程的執(zhí)行,dwmilliseconds表示千分之一秒,所以
sleep(1000);
表示暫停1秒
eg2:
#include
#include using namespace std;dword winapi fun(lpvoid lpparamter)
{
while(1){ cout<<“fun display!”<
}
int main()
{
handle hthread = createthread(null, 0, fun, null, 0, null);
closehandle(hthread);
while(1){ cout<<“main display!”<
return 0;
}
執(zhí)行上述代碼,這次我們可以清楚地看到在屏幕上交錯(cuò)地輸出fun display!和main display!,我們發(fā)現(xiàn)這兩個(gè)函數(shù)確實(shí)是并發(fā)運(yùn)行的,細(xì)心的讀者可能會(huì)發(fā)現(xiàn)我們的程序是每當(dāng)fun函數(shù)和main函數(shù)輸出內(nèi)容后就會(huì)輸出換行,但是我們看到的確是有的時(shí)候程序輸出換行了,有的時(shí)候確沒(méi)有輸出換行,甚至有的時(shí)候是輸出兩個(gè)換行。這是怎么回事?下面我們把程序改一下看看:
eg3:
#include
#include using namespace std;dword winapi fun(lpvoid lpparamter)
{
while(1){ cout<<“fun display!n”;sleep(1000);}
}
int main()
{
handle hthread = createthread(null, 0, fun, null, 0, null);
closehandle(hthread);
while(1){ cout<<“main display!n”;sleep(2000);}
return 0;
}
我們?cè)俅芜\(yùn)行這個(gè)程序,我們發(fā)現(xiàn)這時(shí)候正如我們預(yù)期的,正確地輸出了我們想要輸出的內(nèi)容并且格式也是正確的。下面我就來(lái)講一下此前我們的程序?yàn)槭裁礇](méi)有正確的運(yùn)行。多線程的程序時(shí)并發(fā)地運(yùn)行的,多個(gè)線程之間如果公用了一些資源的話,我們并不能保證這些資源都能正確地被利用,因?yàn)檫@個(gè)時(shí)候資源并不是獨(dú)占的,舉個(gè)例子吧:
eg4:
加入有一個(gè)資源 int a = 3
有一個(gè)線程函數(shù) selfadd()該函數(shù)是使 a += a;
又有一個(gè)線程函數(shù) selfsub()該函數(shù)是使a-= a;
我們假設(shè)上面兩個(gè)線程正在并發(fā)欲行,如果selfadd在執(zhí)行的時(shí)候,我們的目的是想讓a編程6,但此時(shí)selfsub得到了運(yùn)行的機(jī)會(huì),所以a變成了0,等到selfadd的到執(zhí)行的機(jī)會(huì)后,a += a,但是此時(shí)a確是0,并沒(méi)有如我們所預(yù)期的那樣的到6,我們回到前面eg2,在這里,我們可以把屏幕看成是一個(gè)資源,這個(gè)資源被兩個(gè)線程所共用,加入當(dāng)fun函數(shù)輸出了fun display!后,將要輸出endl(也就是清空緩沖區(qū)并換行,在這里我們可以不用理
解什么事緩沖區(qū)),但此時(shí)main函數(shù)確得到了運(yùn)行的機(jī)會(huì),此時(shí)fun函數(shù)還沒(méi)有來(lái)得及輸出換行就把cpu讓給了main函數(shù),而這時(shí)main函數(shù)就直接在fun display!后輸出main display!,至于為什么有的時(shí)候程序會(huì)連續(xù)輸出兩個(gè)換行,讀者可以采用同樣的分析方法來(lái)分析,在這里我就不多講了,留給讀者自己思考了。
那么為什么我們把eg2改成eg3就可以正確的運(yùn)行呢?原因在于,多個(gè)線程雖然是并發(fā)運(yùn)行的,但是有一些操作是必須一氣呵成的,不允許打斷的,所以我們看到eg2和eg3的運(yùn)行結(jié)果是不一樣的。
那么,是不是eg2的代碼我們就不可以讓它正確的運(yùn)行呢?答案當(dāng)然是否,下面我就來(lái)講一下怎樣才能讓eg2的代碼可以正確運(yùn)行。這涉及到多線程的同步問(wèn)題。對(duì)于一個(gè)資源被多個(gè)線程共用會(huì)導(dǎo)致程序的混亂,我們的解決方法是只允許一個(gè)線程擁有對(duì)共享資源的獨(dú)占,這樣就能夠解決上面的問(wèn)題了。
handle createmutex(lpsecurity_attributes lpmutexattributes,// sd
bool binitialowner,// initial owner
lpctstr lpname// object name);
該函數(shù)用于創(chuàng)造一個(gè)獨(dú)占資源,第一個(gè)參數(shù)我們沒(méi)有使用,可以設(shè)為null,第二個(gè)參數(shù)指定該資源初始是否歸屬創(chuàng)建它的進(jìn)程,第三個(gè)參數(shù)指定資源的名稱。
handle hmutex = createmutex(null,true,“screen”);
這條語(yǔ)句創(chuàng)造了一個(gè)名為screen并且歸屬于創(chuàng)建它的進(jìn)程的資源
bool releasemutex(handle hmutex// handle to mutex);
該函數(shù)用于釋放一個(gè)獨(dú)占資源,進(jìn)程一旦釋放該資源,該資源就不再屬于它了,如果還要用到,需要重新申請(qǐng)得到該資源。申請(qǐng)資源的函數(shù)如下
dword waitforsingleobject(handle hhandle,// handle to object
dword dwmilliseconds// time-out interval);
第一個(gè)參數(shù)指定所申請(qǐng)的資源的句柄,第二個(gè)參數(shù)一般指定為infinite,表示如果沒(méi)有申請(qǐng)到資源就一直等待該資源,如果指定為0,表示一旦得不到資源就返回,也可以具體地指定等待多久才返回,單位是千分之一秒。好了,該到我們來(lái)解決eg2的問(wèn)題的時(shí)候了,我們可以把eg2做一些修改,如下
eg5:
#include
#include using namespace std;handle hmutex;
dword winapi fun(lpvoid lpparamter)
{
while(1){
waitforsingleobject(hmutex, infinite);
cout<<“fun display!”<
sleep(1000);
releasemutex(hmutex);
}
}
int main()
{
handle hthread = createthread(null, 0, fun, null, 0, null);hmutex = createmutex(null, false, “screen”);
closehandle(hthread);
while(1){
waitforsingleobject(hmutex, infinite);
cout<<“main display!”<
sleep(2000);
releasemutex(hmutex);
}
return 0;
}
運(yùn)行代碼正如我們所預(yù)期的輸出的內(nèi)容。
java多線程知識(shí)點(diǎn)總結(jié)篇二
java多線程編程總結(jié)
2007-05-17 11:21:59 標(biāo)簽:java 多線程
原創(chuàng)作品,允許轉(zhuǎn)載,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章 原始出處、作者信息和本聲明。否則將追究法律責(zé)任。http:///62575/27069
java多線程編程總結(jié)
下面是java線程系列博文的一個(gè)編目:
java線程:概念與原理 java線程:創(chuàng)建與啟動(dòng)
java線程:線程棧模型與線程的變量 java線程:線程狀態(tài)的轉(zhuǎn)換 java線程:線程的同步與鎖 java線程:線程的交互 java線程:線程的調(diào)度-休眠 java線程:線程的調(diào)度-優(yōu)先級(jí) java線程:線程的調(diào)度-讓步 java線程:線程的調(diào)度-合并 java線程:線程的調(diào)度-守護(hù)線程 java線程:線程的同步-同步方法 java線程:線程的同步-同步塊
java線程:并發(fā)協(xié)作-生產(chǎn)者消費(fèi)者模型 java線程:并發(fā)協(xié)作-死鎖 java線程:volatile關(guān)鍵字 java線程:新特征-線程池
java線程:新特征-有返回值的線程 java線程:新特征-鎖(上)java線程:新特征-鎖(下)java線程:新特征-信號(hào)量 java線程:新特征-阻塞隊(duì)列 java線程:新特征-阻塞棧 java線程:新特征-條件變量 java線程:新特征-原子量 java線程:新特征-障礙器 java線程:大總結(jié)
----
下面的內(nèi)容是很早之前寫(xiě)的,內(nèi)容不夠充實(shí),而且是基于java1.4的內(nèi)容,java5之后,線程并發(fā)部分?jǐn)U展了相當(dāng)多的內(nèi)容,因此建議大家看上面的系列文章的內(nèi)容,與時(shí)俱進(jìn),跟上java發(fā)展的步伐。----
一、認(rèn)識(shí)多任務(wù)、多進(jìn)程、單線程、多線程 要認(rèn)識(shí)多線程就要從操作系統(tǒng)的原理說(shuō)起。
以前古老的dos操作系統(tǒng)(v 6.22)是單任務(wù)的,還沒(méi)有線程的概念,系統(tǒng)在每次只能做一件事情。比如你在copy東西的時(shí)候不能rename文件名。為了提高系統(tǒng)的利用效率,采用批處理來(lái)批量執(zhí)行任務(wù)。
現(xiàn)在的操作系統(tǒng)都是多任務(wù)操作系統(tǒng),每個(gè)運(yùn)行的任務(wù)就是操作系統(tǒng)所做的一件事情,比如你在聽(tīng)歌的同時(shí)還在用msn和好友聊天。聽(tīng)歌和聊天就是兩個(gè)任務(wù),這個(gè)兩個(gè)任務(wù)是“同時(shí)”進(jìn)行的。一個(gè)任務(wù)一般對(duì)應(yīng)一個(gè)進(jìn)程,也可能包含好幾個(gè)進(jìn)程。比如運(yùn)行的msn就對(duì)應(yīng)一個(gè)msn的進(jìn)程,如果你用的是windows系統(tǒng),你就可以在任務(wù)管理器中看到操作系統(tǒng)正在運(yùn)行的進(jìn)程信息。
一般來(lái)說(shuō),當(dāng)運(yùn)行一個(gè)應(yīng)用程序的時(shí)候,就啟動(dòng)了一個(gè)進(jìn)程,當(dāng)然有些會(huì)啟動(dòng)多個(gè)進(jìn)程。啟動(dòng)進(jìn)程的時(shí)候,操作系統(tǒng)會(huì)為進(jìn)程分配資源,其中最主要的資源是內(nèi)存空間,因?yàn)槌绦蚴窃趦?nèi)存中運(yùn)行的。在進(jìn)程中,有些程序流程塊是可以亂序執(zhí)行的,并且這個(gè)代碼塊可以同時(shí)被多次執(zhí)行。實(shí)際上,這樣的代碼塊就是線程體。線程是進(jìn)程中亂序執(zhí)行的代碼流程。當(dāng)多個(gè)線程同時(shí)運(yùn)行的時(shí)候,這樣的執(zhí)行模式成為并發(fā)執(zhí)行。
多線程的目的是為了最大限度的利用cpu資源。
java編寫(xiě)程序都運(yùn)行在在java虛擬機(jī)(jvm)中,在jvm的內(nèi)部,程序的多任務(wù)是通過(guò)線程來(lái)實(shí)現(xiàn)的。每用java命令啟動(dòng)一個(gè)java應(yīng)用程序,就會(huì)啟動(dòng)一個(gè)jvm進(jìn)程。在同一個(gè)jvm進(jìn)程中,有且只有一個(gè)進(jìn)程,就是它自己。在這個(gè)jvm環(huán)境中,所有程序代碼的運(yùn)行都是以線程來(lái)運(yùn)行。
一般常見(jiàn)的java應(yīng)用程序都是單線程的。比如,用java命令運(yùn)行一個(gè)最簡(jiǎn)單的helloworld的java應(yīng)用程序時(shí),就啟動(dòng)了一個(gè)jvm進(jìn)程,jvm找到程序程序的入口點(diǎn)main(),然后運(yùn)行main()方法,這樣就產(chǎn)生了一個(gè)線程,這個(gè)線程稱之為主線程。當(dāng)main方法結(jié)束后,主線程運(yùn)行完成。jvm進(jìn)程也隨即退出。
對(duì)于一個(gè)進(jìn)程中的多個(gè)線程來(lái)說(shuō),多個(gè)線程共享進(jìn)程的內(nèi)存塊,當(dāng)有新的線程產(chǎn)生的時(shí)候,操作系統(tǒng)不分配新的內(nèi)存,而是讓新線程共享原有的進(jìn)程塊的內(nèi)存。因此,線程間的通信很容易,速度也很快。不同的進(jìn)程因?yàn)樘幱诓煌膬?nèi)存塊,因此進(jìn)程之間的通信相對(duì)困難。
實(shí)際上,操作的系統(tǒng)的多進(jìn)程實(shí)現(xiàn)了多任務(wù)并發(fā)執(zhí)行,程序的多線程實(shí)現(xiàn)了進(jìn)程的并發(fā)執(zhí)行。多任務(wù)、多進(jìn)程、多線程的前提都是要求操作系統(tǒng)提供多任務(wù)、多進(jìn)程、多線程的支持。
在java程序中,jvm負(fù)責(zé)線程的調(diào)度。線程調(diào)度是值按照特定的機(jī)制為多個(gè)線程分配cpu的使用權(quán)。調(diào)度的模式有兩種:分時(shí)調(diào)度和搶占式調(diào)度。分時(shí)調(diào)度是所有線程輪流獲得cpu使用權(quán),并平均分配每個(gè)線程占用cpu的時(shí)間;搶占式調(diào)度是根據(jù)線程的優(yōu)先級(jí)別來(lái)獲取cpu的使用權(quán)。jvm的線程調(diào)度模式采用了搶占式模式。
所謂的“并發(fā)執(zhí)行”、“同時(shí)”其實(shí)都不是真正意義上的“同時(shí)”。眾所周知,cpu都有個(gè)時(shí)鐘頻率,表示每秒中能執(zhí)行cpu指令的次數(shù)。在每個(gè)時(shí)鐘周期內(nèi),cpu實(shí)際上只能去執(zhí)行一條(也有可能多條)指令。操作系統(tǒng)將進(jìn)程線程進(jìn)行管理,輪流(沒(méi)有固定的順序)分配每個(gè)進(jìn)程很短的一段是時(shí)間(不一定是均分),然后在每個(gè)線程內(nèi)部,程序代碼自己處理該進(jìn)程內(nèi)部線程的時(shí)間分配,多個(gè)線程之間相互的切換去執(zhí)行,這個(gè)切換時(shí)間也是非常短的。因此多任務(wù)、多進(jìn)程、多線程都是操作系統(tǒng)給人的一種宏觀感受,從微觀角度看,程序的運(yùn)行是異步執(zhí)行的。
用一句話做總結(jié):雖然操作系統(tǒng)是多線程的,但cpu每一時(shí)刻只能做一件事,和人的大腦是一樣的,呵呵。
二、java與多線程
java語(yǔ)言的多線程需要操作系統(tǒng)的支持。
java 虛擬機(jī)允許應(yīng)用程序并發(fā)地運(yùn)行多個(gè)執(zhí)行線程。java語(yǔ)言提供了多線程編程的擴(kuò)展點(diǎn),并給出了功能強(qiáng)大的線程控制api。
在java中,多線程的實(shí)現(xiàn)有兩種方式: 類 le接口
每個(gè)線程都有一個(gè)優(yōu)先級(jí),高優(yōu)先級(jí)線程的執(zhí)行優(yōu)先于低優(yōu)先級(jí)線程。每個(gè)線程都可以或不可以標(biāo)記為一個(gè)守護(hù)程序。當(dāng)某個(gè)線程中運(yùn)行的代碼創(chuàng)建一個(gè)新 thread 對(duì)象時(shí),該新線程的初始優(yōu)先級(jí)被設(shè)定為創(chuàng)建線程的優(yōu)先級(jí),并且當(dāng)且僅當(dāng)創(chuàng)建線程是守護(hù)線程時(shí),新線程才是守護(hù)程序。
當(dāng) java 虛擬機(jī)啟動(dòng)時(shí),通常都會(huì)有單個(gè)非守護(hù)線程(它通常會(huì)調(diào)用某個(gè)指定類的 main 方法)。java 虛擬機(jī)會(huì)繼續(xù)執(zhí)行線程,直到下列任一情況出現(xiàn)時(shí)為止:
調(diào)用了 runtime 類的 exit 方法,并且安全管理器允許退出操作發(fā)生。
非守護(hù)線程的所有線程都已停止運(yùn)行,無(wú)論是通過(guò)從對(duì) run 方法的調(diào)用中返回,還是通過(guò)拋出一個(gè)傳播到 run 方法之外的異常。
三、類
/** * file name: * created by: intellij idea.* copyright: copyright(c)2003-2006 * company: lavasoft([url]http:///[/url])* author: leizhimin * modifier: leizhimin * date time: 2007-5-17 10:03:12 * readme: 通過(guò)擴(kuò)展thread類實(shí)現(xiàn)多線程 */ public class testmitithread { public static void main(string[] rags){ n(tthread().getname()+ “ 線程運(yùn)行開(kāi)始!”);new mitisay(“a”).start();new mitisay(“b”).start();n(tthread().getname()+ “ 線程運(yùn)行結(jié)束!”);} }
class mitisay extends thread { public mitisay(string threadname){ super(threadname);}
public void run(){ n(getname()+ “ 線程運(yùn)行開(kāi)始!”);for(int i = 0;i < 10;i++){ n(i + “ ” + getname());try { sleep((int)()* 10);} catch(interruptedexception e){ tacktrace();} } n(getname()+ “ 線程運(yùn)行結(jié)束!”);} }
運(yùn)行結(jié)果:
main 線程運(yùn)行開(kāi)始!main 線程運(yùn)行結(jié)束!a 線程運(yùn)行開(kāi)始!0 a 1 a b 線程運(yùn)行開(kāi)始!2 a 0 b 3 a 4 a 1 b 5 a 6 a 7 a 8 a 9 a a 線程運(yùn)行結(jié)束!2 b 3 b 4 b 5 b 6 b 7 b 8 b 9 b b 線程運(yùn)行結(jié)束!說(shuō)明:
程序啟動(dòng)運(yùn)行main時(shí)候,java虛擬機(jī)啟動(dòng)一個(gè)進(jìn)程,主線程main在main()調(diào)用時(shí)候被創(chuàng)建。隨著調(diào)用mitisay的兩個(gè)對(duì)象的start方法,另外兩個(gè)線程也啟動(dòng)了,這樣,整個(gè)應(yīng)用就在多線程下運(yùn)行。
在一個(gè)方法中調(diào)用tthread().getname()方法,可以獲取當(dāng)前線程的名字。在mian方法中調(diào)用該方法,獲取的是主線程的名字。
注意:start()方法的調(diào)用后并不是立即執(zhí)行多線程代碼,而是使得該線程變?yōu)榭蛇\(yùn)行態(tài)(runnable),什么時(shí)候運(yùn)行是由操作系統(tǒng)決定的。
從程序運(yùn)行的結(jié)果可以發(fā)現(xiàn),多線程程序是亂序執(zhí)行。因此,只有亂序執(zhí)行的代碼才有必要設(shè)計(jì)為多線程。
()方法調(diào)用目的是不讓當(dāng)前線程獨(dú)自霸占該進(jìn)程所獲取的cpu資源,以留出一定時(shí)間給其他線程執(zhí)行的機(jī)會(huì)。
實(shí)際上所有的多線程代碼執(zhí)行順序都是不確定的,每次執(zhí)行的結(jié)果都是隨機(jī)的。
四、le接口
/** * 通過(guò)實(shí)現(xiàn) runnable 接口實(shí)現(xiàn)多線程 */ public class testmitithread1 implements runnable {
public static void main(string[] args){ n(tthread().getname()+ “ 線程運(yùn)行開(kāi)始!”);testmitithread1 test = new testmitithread1();thread thread1 = new thread(test);thread thread2 = new thread(test);();();n(tthread().getname()+ “ 線程運(yùn)行結(jié)束!”);}
public void run(){ n(tthread().getname()+ “ 線程運(yùn)行開(kāi)始!”);for(int i = 0;i < 10;i++){ n(i + “ ” + tthread().getname());try { ((int)()* 10);} catch(interruptedexception e){ tacktrace();} } n(tthread().getname()+ “ 線程運(yùn)行結(jié)束!”);} }
運(yùn)行結(jié)果:
main 線程運(yùn)行開(kāi)始!thread-0 線程運(yùn)行開(kāi)始!main 線程運(yùn)行結(jié)束!0 thread-0 thread-1 線程運(yùn)行開(kāi)始!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 線程運(yùn)行結(jié)束!8 thread-1 9 thread-1 thread-1 線程運(yùn)行結(jié)束!說(shuō)明:
testmitithread1類通過(guò)實(shí)現(xiàn)runnable接口,使得該類有了多線程類的特征。run()方法是多線程程序的一個(gè)約定。所有的多線程代碼都在run方法里面。thread類實(shí)際上也是實(shí)現(xiàn)了runnable接口的類。
在啟動(dòng)的多線程的時(shí)候,需要先通過(guò)thread類的構(gòu)造方法thread(runnable target)構(gòu)造出對(duì)象,然后調(diào)用thread對(duì)象的start()方法來(lái)運(yùn)行多線程代碼。
實(shí)際上所有的多線程代碼都是通過(guò)運(yùn)行thread的start()方法來(lái)運(yùn)行的。因此,不管是擴(kuò)展thread類還是實(shí)現(xiàn)runnable接口來(lái)實(shí)現(xiàn)多線程,最終還是通過(guò)thread的對(duì)象的api來(lái)控制線程的,熟悉thread類的api是進(jìn)行多線程編程的基礎(chǔ)。
五、讀解thread類api
static int max_priority 線程可以具有的最高優(yōu)先級(jí)。static int min_priority 線程可以具有的最低優(yōu)先級(jí)。static int norm_priority 分配給線程的默認(rèn)優(yōu)先級(jí)。
構(gòu)造方法摘要
thread(runnable target)分配新的 thread 對(duì)象。thread(string name)分配新的 thread 對(duì)象。
方法摘要
static thread currentthread()返回對(duì)當(dāng)前正在執(zhí)行的線程對(duì)象的引用。classloader getcontextclassloader()返回該線程的上下文 classloader。long getid()返回該線程的標(biāo)識(shí)符。string getname()返回該線程的名稱。int getpriority()返回線程的優(yōu)先級(jí)。 getstate()返回該線程的狀態(tài)。threadgroup getthreadgroup()返回該線程所屬的線程組。static boolean holdslock(object obj)當(dāng)且僅當(dāng)當(dāng)前線程在指定的對(duì)象上保持監(jiān)視器鎖時(shí),才返回 true。void interrupt()中斷線程。
static boolean interrupted()測(cè)試當(dāng)前線程是否已經(jīng)中斷。boolean isalive()測(cè)試線程是否處于活動(dòng)狀態(tài)。boolean isdaemon()測(cè)試該線程是否為守護(hù)線程。boolean isinterrupted()測(cè)試線程是否已經(jīng)中斷。void join()等待該線程終止。void join(long millis)等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒。void join(long millis, int nanos)等待該線程終止的時(shí)間最長(zhǎng)為 millis 毫秒 + nanos 納秒。void resume()已過(guò)時(shí)。該方法只與 suspend()一起使用,但 suspend()已經(jīng)遭到反對(duì),因?yàn)樗哂兴梨i傾向。有關(guān)更多信息,請(qǐng)參閱為何 、d 和 遭到反對(duì)?。void run()如果該線程是使用獨(dú)立的 runnable 運(yùn)行對(duì)象構(gòu)造的,則調(diào)用該 runnable 對(duì)象的 run 方法;否則,該方法不執(zhí)行任何操作并返回。void setcontextclassloader(classloader cl)設(shè)置該線程的上下文 classloader。void setdaemon(boolean on)將該線程標(biāo)記為守護(hù)線程或用戶線程。
static void setdefaultuncaughtexceptionhandler(htexceptionhandler eh)設(shè)置當(dāng)線程由于未捕獲到異常而突然終止,并且沒(méi)有為該線程定義其他處理程序時(shí)所調(diào)用的默認(rèn)處理程序。void setname(string name)改變線程名稱,使之與參數(shù) name 相同。void setpriority(int newpriority)更改線程的優(yōu)先級(jí)。
void setuncaughtexceptionhandler(htexceptionhandler eh)設(shè)置該線程由于未捕獲到異常而突然終止時(shí)調(diào)用的處理程序。static void sleep(long millis)在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)。static void sleep(long millis, int nanos)在指定的毫秒數(shù)加指定的納秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)。void start()使該線程開(kāi)始執(zhí)行;java 虛擬機(jī)調(diào)用該線程的 run 方法。void stop()已過(guò)時(shí)。該方法具有固有的不安全性。用 來(lái)終止線程將釋放它已經(jīng)鎖定的所有監(jiān)視器(作為沿堆棧向上傳播的未檢查 threaddeath 異常的一個(gè)自然后果)。如果以前受這些監(jiān)視器保護(hù)的任何對(duì)象都處于一種不一致的狀態(tài),則損壞的對(duì)象將對(duì)其他線程可見(jiàn),這有可能導(dǎo)致任意的行為。stop 的許多使用都應(yīng)由只修改某些變量以指示目標(biāo)線程應(yīng)該停止運(yùn)行的代碼來(lái)取代。目標(biāo)線程應(yīng)定期檢查該變量,并且如果該變量指示它要停止運(yùn)行,則從其運(yùn)行方法依次返回。如果目標(biāo)線程等待很長(zhǎng)時(shí)間(例如基于一個(gè)條件變量),則應(yīng)使用 interrupt 方法來(lái)中斷該等待。有關(guān)更多信息,請(qǐng)參閱《為何不贊成使用 、d 和 ?》。void stop(throwable obj)已過(guò)時(shí)。該方法具有固有的不安全性。請(qǐng)參閱 stop()以獲得詳細(xì)信息。該方法的附加危險(xiǎn)是它可用于生成目標(biāo)線程未準(zhǔn)備處理的異常(包括若沒(méi)有該方法該線程不太可能拋出的已檢查的異常)。有關(guān)更多信息,請(qǐng)參閱為何 、d 和 遭到反對(duì)?。void suspend()已過(guò)時(shí)。該方法已經(jīng)遭到反對(duì),因?yàn)樗哂泄逃械乃梨i傾向。如果目標(biāo)線程掛起時(shí)在保護(hù)關(guān)鍵系統(tǒng)資源的監(jiān)視器上保持有鎖,則在目標(biāo)線程重新開(kāi)始以前任何線程都不能訪問(wèn)該資源。如果重新開(kāi)始目標(biāo)線程的線程想在調(diào)用 resume 之前鎖定該監(jiān)視器,則會(huì)發(fā)生死鎖。這類死鎖通常會(huì)證明自己是“凍結(jié)”的進(jìn)程。有關(guān)更多信息,請(qǐng)參閱為何 、d 和 遭到反對(duì)?。string tostring()返回該線程的字符串表示形式,包括線程名稱、優(yōu)先級(jí)和線程組。static void yield()暫停當(dāng)前正在執(zhí)行的線程對(duì)象,并執(zhí)行其他線程。
六、線程的狀態(tài)轉(zhuǎn)換圖
線程在一定條件下,狀態(tài)會(huì)發(fā)生變化。線程變化的狀態(tài)轉(zhuǎn)換圖如下:
1、新建狀態(tài)(new):新創(chuàng)建了一個(gè)線程對(duì)象。
2、就緒狀態(tài)(runnable):線程對(duì)象創(chuàng)建后,其他線程調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中,變得可運(yùn)行,等待獲取cpu的使用權(quán)。
3、運(yùn)行狀態(tài)(running):就緒狀態(tài)的線程獲取了cpu,執(zhí)行程序代碼。
4、阻塞狀態(tài)(blocked):阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄cpu使用權(quán),暫時(shí)停止運(yùn)行。直到線程進(jìn)入就緒狀態(tài),才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:
(一)、等待阻塞:運(yùn)行的線程執(zhí)行wait()方法,jvm會(huì)把該線程放入等待池中。
(二)、同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線程占用,則jvm會(huì)把該線程放入鎖池中。
(三)、其他阻塞:運(yùn)行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了i/o請(qǐng)求時(shí),jvm會(huì)把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者i/o處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。
5、死亡狀態(tài)(dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。
七、線程的調(diào)度
1、調(diào)整線程優(yōu)先級(jí):java線程有優(yōu)先級(jí),優(yōu)先級(jí)高的線程會(huì)獲得較多的運(yùn)行機(jī)會(huì)。
java線程的優(yōu)先級(jí)用整數(shù)表示,取值范圍是1~10,thread類有以下三個(gè)靜態(tài)常量: static int max_priority 線程可以具有的最高優(yōu)先級(jí),取值為10。static int min_priority 線程可以具有的最低優(yōu)先級(jí),取值為1。static int norm_priority 分配給線程的默認(rèn)優(yōu)先級(jí),取值為5。
thread類的setpriority()和getpriority()方法分別用來(lái)設(shè)置和獲取線程的優(yōu)先級(jí)。
每個(gè)線程都有默認(rèn)的優(yōu)先級(jí)。_priority。
線程的優(yōu)先級(jí)有繼承關(guān)系,比如a線程中創(chuàng)建了b線程,那么b將和a具有相同的優(yōu)先級(jí)。jvm提供了10個(gè)線程優(yōu)先級(jí),但與常見(jiàn)的操作系統(tǒng)都不能很好的映射。如果希望程序能移植到各個(gè)操作系統(tǒng)中,應(yīng)該僅僅使用thread類有以下三個(gè)靜態(tài)常量作為優(yōu)先級(jí),這樣能保證同樣的優(yōu)先級(jí)采用了同樣的調(diào)度方式。
2、線程睡眠:(long millis)方法,使線程轉(zhuǎn)到阻塞狀態(tài)。millis參數(shù)設(shè)定睡眠的時(shí)間,以毫秒為單位。當(dāng)睡眠結(jié)束后,就轉(zhuǎn)為就緒(runnable)狀態(tài)。sleep()平臺(tái)移植性好。
3、線程等待:object類中的wait()方法,導(dǎo)致當(dāng)前的線程等待,直到其他線程調(diào)用此對(duì)象的 notify()方法或 notifyall()喚醒方法。這個(gè)兩個(gè)喚醒方法也是object類中的方法,行為等價(jià)于調(diào)用 wait(0)一樣。
4、線程讓步:()方法,暫停當(dāng)前正在執(zhí)行的線程對(duì)象,把執(zhí)行機(jī)會(huì)讓給相同或者更高優(yōu)先級(jí)的線程。
5、線程加入:join()方法,等待其他線程終止。在當(dāng)前線程中調(diào)用另一個(gè)線程的join()方法,則當(dāng)前線程轉(zhuǎn)入阻塞狀態(tài),直到另一個(gè)進(jìn)程運(yùn)行結(jié)束,當(dāng)前線程再由阻塞轉(zhuǎn)為就緒狀態(tài)。
6、線程喚醒:object類中的notify()方法,喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程。如果所有線程都在此對(duì)象上等待,則會(huì)選擇喚醒其中一個(gè)線程。選擇是任意性的,并在對(duì)實(shí)現(xiàn)做出決定時(shí)發(fā)生。線程通過(guò)調(diào)用其中一個(gè) wait 方法,在對(duì)象的監(jiān)視器上等待。直到當(dāng)前的線程放棄此對(duì)象上的鎖定,才能繼續(xù)執(zhí)行被喚醒的線程。被喚醒的線程將以常規(guī)方式與在該對(duì)象上主動(dòng)同步的其他所有線程進(jìn)行競(jìng)爭(zhēng);例如,喚醒的線程在作為鎖定此對(duì)象的下一個(gè)線程方面沒(méi)有可靠的特權(quán)或劣勢(shì)。類似的方法還有一個(gè)notifyall(),喚醒在此對(duì)象監(jiān)視器上等待的所有線程。注意:thread中suspend()和resume()兩個(gè)方法在jdk1.5中已經(jīng)廢除,不再介紹。因?yàn)橛兴梨i傾向。
7、常見(jiàn)線程名詞解釋
主線程:jvm調(diào)用程序mian()所產(chǎn)生的線程。
當(dāng)前線程:這個(gè)是容易混淆的概念。tthread()來(lái)獲取的進(jìn)程。后臺(tái)線程:指為其他線程提供服務(wù)的線程,也稱為守護(hù)線程。jvm的垃圾回收線程就是一個(gè)后臺(tái)線程。
前臺(tái)線程:是指接受后臺(tái)線程服務(wù)的線程,其實(shí)前臺(tái)后臺(tái)線程是聯(lián)系在一起,就像傀儡和幕后操縱者一樣的關(guān)系??苁乔芭_(tái)線程、幕后操縱者是后臺(tái)線程。由前臺(tái)線程創(chuàng)建的線程默認(rèn)也是前臺(tái)線程。可以通過(guò)isdaemon()和setdaemon()方法來(lái)判斷和設(shè)置一個(gè)線程是否為后臺(tái)線程。
本文出自 “熔 巖” 博客,請(qǐng)務(wù)必保留此出處http:///62575/27069
java多線程知識(shí)點(diǎn)總結(jié)篇三
多線程編程
一、問(wèn)題的提出
1.1問(wèn)題的引出
編寫(xiě)一個(gè)耗時(shí)的單線程程序:
新建一個(gè)基于對(duì)話框的應(yīng)用程序singlethread,在主對(duì)話框idd_singlethread_dialog添加一個(gè)按鈕,id為idc_sleep_six_second,標(biāo)題為“延時(shí)6秒”,添加按鈕的響應(yīng)函數(shù),代碼如下:
void csinglethreaddlg::onsleepsixsecond(){ sleep(6000);//延時(shí)6秒 } 編譯并運(yùn)行應(yīng)用程序,單擊“延時(shí)6秒”按鈕,你就會(huì)發(fā)現(xiàn)在這6秒期間程序就象“死機(jī)”一樣,不在響應(yīng)其它消息。為了更好地處理這種耗時(shí)的操作,我們有必要學(xué)習(xí)——多線程編程。
1.2多線程概述
進(jìn)程和線程都是操作系統(tǒng)的概念。進(jìn)程是應(yīng)用程序的執(zhí)行實(shí)例,每個(gè)進(jìn)程是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它各種系統(tǒng)資源組成,進(jìn)程在運(yùn)行過(guò)程中創(chuàng)建的資源隨著進(jìn)程的終止而被銷毀,所使用的系統(tǒng)資源在進(jìn)程終止時(shí)被釋放或關(guān)閉。
線程是進(jìn)程內(nèi)部的一個(gè)執(zhí)行單元。系統(tǒng)創(chuàng)建好進(jìn)程后,實(shí)際上就啟動(dòng)執(zhí)行了該進(jìn)程的主執(zhí)行線程,主執(zhí)行線程以函數(shù)地址形式,比如說(shuō)main或winmain函數(shù),將程序的啟動(dòng)點(diǎn)提供給windows系統(tǒng)。主執(zhí)行線程終止了,進(jìn)程也就隨之終止。
每一個(gè)進(jìn)程至少有一個(gè)主執(zhí)行線程,它無(wú)需由用戶去主動(dòng)創(chuàng)建,是由系統(tǒng)自動(dòng)創(chuàng)建的。用戶根據(jù)需要在應(yīng)用程序中創(chuàng)建其它線程,多個(gè)線程并發(fā)地運(yùn)行于同一個(gè)進(jìn)程中。一個(gè)進(jìn)程中的所有線程都在該進(jìn)程的虛擬地址空間中,共同使用這些虛擬地址空間、全局變量和系統(tǒng)資源,所以線程間的通訊非常方便,多線程技術(shù)的應(yīng)用也較為廣泛。
多線程可以實(shí)現(xiàn)并行處理,避免了某項(xiàng)任務(wù)長(zhǎng)時(shí)間占用cpu時(shí)間。要說(shuō)明的一點(diǎn)是,對(duì)于單處理器(cpu)的,為了運(yùn)行所有這些線程,操作系統(tǒng)為每個(gè)獨(dú)立線程安排一些cpu時(shí)間,操作系統(tǒng)以輪換方式向線程提供時(shí)間片,這就給人一種假象,好象這些線程都在同時(shí)運(yùn)行。由此可見(jiàn),如果兩個(gè)非?;钴S的線程為了搶奪對(duì)cpu的控制權(quán),在線程切換時(shí)會(huì)消耗很多的cpu資源,反而會(huì)降低系統(tǒng)的性能。這一點(diǎn)在多線程編程時(shí)應(yīng)該注意。
win32 sdk函數(shù)支持進(jìn)行多線程的程序設(shè)計(jì),并提供了操作系統(tǒng)原理中的各種同步、互斥和臨界區(qū)等操作。visual c++中,使用mfc類庫(kù)也實(shí)現(xiàn)了多線程的程序設(shè)計(jì),使得多線程編程更加方便。1.3 win32 api對(duì)多線程編程的支持
win32 提供了一系列的api函數(shù)來(lái)完成線程的創(chuàng)建、掛起、恢復(fù)、終結(jié)以及通信等工作。下面將選取其中的一些重要函數(shù)進(jìn)行說(shuō)明。
1、handle createthread(lpsecurity_attributes lpthreadattributes,dword dwstacksize,lpthread_start_routine lpstartaddress,lpvoid lpparameter,dword dwcreationflags,lpdword lpthreadid);該函數(shù)在其調(diào)用進(jìn)程的進(jìn)程空間里創(chuàng)建一個(gè)新的線程,并返回已建線程的句柄,其中各參數(shù)說(shuō)明如下:
lpthreadattributes:指向一個(gè) security_attributes 結(jié)構(gòu)的指針,該結(jié)構(gòu)決定了線程的安全屬性,一般置為 null;
dwstacksize:指定了線程的堆棧深度,一般都設(shè)置為0;
lpstartaddress:表示新線程開(kāi)始執(zhí)行時(shí)代碼所在函數(shù)的地址,即線程的起始地址。一般情況為(lpthread_start_routine)threadfunc,threadfunc 是線程函數(shù)名;
lpparameter:指定了線程執(zhí)行時(shí)傳送給線程的32位參數(shù),即線程函數(shù)的參數(shù);
dwcreationflags:控制線程創(chuàng)建的附加標(biāo)志,可以取兩種值。如果該參數(shù)為0,線程在被創(chuàng)建后就會(huì)立即開(kāi)始執(zhí)行;如果該參數(shù)為create_suspended,則系統(tǒng)產(chǎn)生線程后,該線程處于掛起狀態(tài),并不馬上執(zhí)行,直至函數(shù)resumethread被調(diào)用;
lpthreadid:該參數(shù)返回所創(chuàng)建線程的id;
如果創(chuàng)建成功則返回線程的句柄,否則返回null。
2、dword suspendthread(handle hthread);該函數(shù)用于掛起指定的線程,如果函數(shù)執(zhí)行成功,則線程的執(zhí)行被終止。
3、dword resumethread(handle hthread);該函數(shù)用于結(jié)束線程的掛起狀態(tài),執(zhí)行線程。
4、void exitthread(dword dwexitcode);該函數(shù)用于線程終結(jié)自身的執(zhí)行,主要在線程的執(zhí)行函數(shù)中被調(diào)用。其中參數(shù)dwexitcode用來(lái)設(shè)置線程的退出碼。
5、bool terminatethread(handle hthread,dword dwexitcode);
一般情況下,線程運(yùn)行結(jié)束之后,線程函數(shù)正常返回,但是應(yīng)用程序可以調(diào)用terminatethread強(qiáng)行終止某一線程的執(zhí)行。各參數(shù)含義如下: hthread:將被終結(jié)的線程的句柄;
dwexitcode:用于指定線程的退出碼。
使用terminatethread()終止某個(gè)線程的執(zhí)行是不安全的,可能會(huì)引起系統(tǒng)不穩(wěn)定;雖然該函數(shù)立即終止線程的執(zhí)行,但并不釋放線程所占用的資源。因此,一般不建議使用該函數(shù)。
6、bool postthreadmessage(dword idthread,uint msg,wparam wparam,lparam lparam);該函數(shù)將一條消息放入到指定線程的消息隊(duì)列中,并且不等到消息被該線程處理時(shí)便返回。idthread:將接收消息的線程的id;
msg:指定用來(lái)發(fā)送的消息;
wparam:同消息有關(guān)的字參數(shù);
lparam:同消息有關(guān)的長(zhǎng)參數(shù);
調(diào)用該函數(shù)時(shí),如果即將接收消息的線程沒(méi)有創(chuàng)建消息循環(huán),則該函數(shù)執(zhí)行失敗。
32 api多線程編程例程
例程1 [multithread1] 一個(gè)簡(jiǎn)單的線程。注意事項(xiàng):
? volatile:關(guān)鍵字:
volatile是要求c++編譯器不要自作聰明的把變量緩沖在寄存器里.因?yàn)樵撟兞靠赡軙?huì)被意外的修改。(多個(gè)線程或其他原因)
如從串口讀數(shù)據(jù)的場(chǎng)合,把變量緩沖在寄存器里,le也加上,以保證從內(nèi)存中讀取變量的值.? 終止線程:
windows終止線程運(yùn)行的四種方法 終止線程運(yùn)行
若要終止線程的運(yùn)行,可以使用下面的方法:
? 線程函數(shù)返回(最好使用這種方法)。
? 通過(guò)調(diào)用 exitthread 函數(shù),線程將自行撤消(最好不要使用這種方法)。
? 同一個(gè)進(jìn)程或另一個(gè)進(jìn)程中的線程調(diào)用 terminatethread 函數(shù)(應(yīng)該避免使用這種方法)。
? 包含線程的進(jìn)程終止運(yùn)行(應(yīng)該避免使用這種方法)。
下面將介紹終止線程運(yùn)行的方法,并且說(shuō)明線程終止運(yùn)行時(shí)會(huì)出現(xiàn)什么情況。
? 線程函數(shù)返回
始終都應(yīng)該將線程設(shè)計(jì)成這樣的形式,即當(dāng)想要線程終止運(yùn)行時(shí),它們就能夠返回。這是確保所有線程資源被正確地清除的唯一辦法。
如果線程能夠返回,就可以確保下列事項(xiàng)的實(shí)現(xiàn):
? 在線程函數(shù)中創(chuàng)建的所有 c++ 對(duì)象均將通過(guò)它們的撤消函數(shù)正確地撤消。
? 操作系統(tǒng)將正確地釋放線程堆棧使用的內(nèi)存。
? 系統(tǒng)將線程的退出代碼(在線程的內(nèi)核對(duì)象中維護(hù))設(shè)置為線程函數(shù)的返回值。
? 系統(tǒng)將遞減線程內(nèi)核對(duì)象的使用計(jì)數(shù)。? 使用 exitthread 函數(shù)
可以讓線程調(diào)用 exitthread 函數(shù),以便強(qiáng)制線程終止運(yùn)行:
void exitthread(dword dwexitcode);
該函數(shù)將終止線程的運(yùn)行,并導(dǎo)致操作系統(tǒng)清除該線程使用的所有操作系統(tǒng)資源。但是,c++ 資源(如 c++ 類對(duì)象)將不被撤消。由于這個(gè)原因,最好從線程函數(shù)返回,而不是通過(guò)調(diào)用 exitthread 來(lái)返回。
當(dāng)然,可以使用 exitthread 的 dwexitthread 參數(shù)告訴系統(tǒng)將線程的退出代碼設(shè)置為什么。exitthread 函數(shù)并不返回任何值,因?yàn)榫€程已經(jīng)終止運(yùn)行,不能執(zhí)行更多的代碼。? 使用 terminatethread 函數(shù)
調(diào)用 terminatethread 函數(shù)也能夠終止線程的運(yùn)行:
bool terminatethread(handle hthread, dword dwexitcode);
與 exitthread 不同,exitthread 總是撤消調(diào)用的線程,而 terminatethread 能夠撤消任何線程。hthread 參數(shù)用于標(biāo)識(shí)被終止運(yùn)行的線程的句柄。當(dāng)線程終止運(yùn)行時(shí),它的退出代碼成為你作為 dwexitcode 參數(shù)傳遞的值。同時(shí),線程的內(nèi)核對(duì)象的使用計(jì)數(shù)也被遞減。
注意 terminatethread 函數(shù)是異步運(yùn)行的函數(shù),也就是說(shuō),它告訴系統(tǒng)你想要線程終止運(yùn)行,但是,當(dāng)函數(shù)返回時(shí),不能保證線程被撤消。如果需要確切地知道該線程已經(jīng)終止運(yùn)行,必須調(diào)用 waitforsingleobject 或者類似的函數(shù),傳遞線程的句柄。
設(shè)計(jì)良好的應(yīng)用程序從來(lái)不使用這個(gè)函數(shù),因?yàn)楸唤K止運(yùn)行的線程收不到它被撤消的通知。線程不能正確地清除,并且不能防止自己被撤消。
注意 當(dāng)使用返回或調(diào)用 exitthread 的方法撤消線程時(shí),該線程的內(nèi)存堆棧也被撤消。但是,如果使用 terminatethread,那么在擁有線程的進(jìn)程終止運(yùn)行之前,系統(tǒng)不撤消該線程的堆棧。microsoft故意用這種方法來(lái)實(shí)現(xiàn) terminatethread。如果其他仍然正在執(zhí)行的線程要引用強(qiáng)制撤消的線程堆棧上的值,那么其他的線程就會(huì)出現(xiàn)訪問(wèn)違規(guī)的問(wèn)題。如果將已經(jīng)撤消的線程的堆棧留在內(nèi)存中,那么其他線程就可以繼續(xù)很好地運(yùn)行。
此外,當(dāng)線程終止運(yùn)行時(shí),dll 通常接收通知。如果使用 terminatethread 強(qiáng)迫線程終止,dll 就不接收通知,這能阻止適當(dāng)?shù)那宄?,在進(jìn)程終止運(yùn)行時(shí)撤消線程。當(dāng)線程終止運(yùn)行時(shí),會(huì)發(fā)生下列操作:
? 線程擁有的所有用戶對(duì)象均被釋放。在 windows 中,大多數(shù)對(duì)象是由包含創(chuàng)建這些對(duì)象的線程的進(jìn)程擁有的。但是一個(gè)線程擁有兩個(gè)用戶對(duì)象,即窗口和掛鉤。當(dāng)線程終止運(yùn)行時(shí),系統(tǒng)會(huì)自動(dòng)撤消任何窗口,并且卸載線程創(chuàng)建的或安裝的任何掛鉤。其他對(duì)象只有在擁有線程的進(jìn)程終止運(yùn)行時(shí)才被撤消。
? 線程的退出代碼從 still_active 改為傳遞給 exitthread 或 terminatethread 的代碼。
? 線程內(nèi)核對(duì)象的狀態(tài)變?yōu)橐淹ㄖ?/p>
? 如果線程是進(jìn)程中最后一個(gè)活動(dòng)線程,系統(tǒng)也將進(jìn)程視為已經(jīng)終止運(yùn)行。
? 線程內(nèi)核對(duì)象的使用計(jì)數(shù)遞減 1。
當(dāng)一個(gè)線程終止運(yùn)行時(shí),在與它相關(guān)聯(lián)的線程內(nèi)核對(duì)象的所有未結(jié)束的引用關(guān)閉之前,該內(nèi)核對(duì)象不會(huì)自動(dòng)被釋放。
一旦線程不再運(yùn)行,系統(tǒng)中就沒(méi)有別的線程能夠處理該線程的句柄。然而別的線程可以調(diào)用 getexitcodethread 來(lái)檢查由 hthread 標(biāo)識(shí)的線程是否已經(jīng)終止運(yùn)行。如果它已經(jīng)終止運(yùn)行,則確定它的退出代碼:
bool getexitcodethread(handle hthread, pdowrd pdwexitcode);退出代碼的值在 pdwexitcode 指向的 dword 中返回。如果調(diào)用 getexitcodethread 時(shí)線程尚未終止運(yùn)行,該函數(shù)就用 still_active 標(biāo)識(shí)符(定義為 0x103)填入 dword。如果該函數(shù)運(yùn)行成功,便返回 true。
? 線程的定義:
例程2[multithread2] 傳送一個(gè)一個(gè)整型的參數(shù)到一個(gè)線程中,以及如何等待一個(gè)線程完成處理。
dword waitforsingleobject(handle hhandle,dword dwmilliseconds);
hhandle:為要監(jiān)視的對(duì)象(一般為同步對(duì)象,也可以是線程)的句柄;
dwmilliseconds:為hhandle對(duì)象所設(shè)置的超時(shí)值,單位為毫秒;
當(dāng)在某一線程中調(diào)用該函數(shù)時(shí),線程暫時(shí)掛起,系統(tǒng)監(jiān)視hhandle所指向的對(duì)象的狀態(tài)。如果在掛起的dwmilliseconds毫秒內(nèi),線程所等待的對(duì)象變?yōu)橛行盘?hào)狀態(tài),則該函數(shù)立即返回;如果超時(shí)時(shí)間已經(jīng)到達(dá)dwmilliseconds毫秒,但hhandle所指向的對(duì)象還沒(méi)有變成有信號(hào)狀態(tài),函數(shù)照樣返回。參數(shù)dwmilliseconds有兩個(gè)具有特殊意義的值:0和infinite。若為0,則該函數(shù)立即返回;若為infinite,則線程一直被掛起,直到hhandle所指向的對(duì)象變?yōu)橛行盘?hào)狀態(tài)時(shí)為止。
例程3[multithread3] 傳送一個(gè)結(jié)構(gòu)體給一個(gè)線程函數(shù),可以通過(guò)傳送一個(gè)指向結(jié)構(gòu)體的指針參數(shù)來(lái)完成。補(bǔ)充一點(diǎn):如果你在void cmultithread3dlg::onstart()函數(shù)中添加/* */語(yǔ)句,編譯運(yùn)行你就會(huì)發(fā)現(xiàn)進(jìn)度條不進(jìn)行刷新,主線程也停止了反應(yīng)。什么原因呢?這是因?yàn)閣aitforsingleobject函數(shù)等待子線程(threadfunc)結(jié)束時(shí),導(dǎo)致了線程死鎖。因?yàn)閣aitforsingleobject函數(shù)會(huì)將主線程掛起(任何消息都得不到處理),而子線程threadfunc正在設(shè)置進(jìn)度條,一直在等待主線程將刷新消息處理完畢返回才會(huì)檢測(cè)通知事件。這樣兩個(gè)線程都在互相等待,死鎖發(fā)生了,編程時(shí)應(yīng)注意避免。
例程4[multithread4] 測(cè)試在windows下最多可創(chuàng)建線程的數(shù)目。
二、mfc中的多線程開(kāi)發(fā)
2.1 mfc對(duì)多線程編程的支持
mfc中有兩類線程,分別稱之為工作者線程和用戶界面線程。二者的主要區(qū)別在于工作者線程沒(méi)有消息循環(huán),而用戶界面線程有自己的消息隊(duì)列和消息循環(huán)。
工作者線程沒(méi)有消息機(jī)制,通常用來(lái)執(zhí)行后臺(tái)計(jì)算和維護(hù)任務(wù),如冗長(zhǎng)的計(jì)算過(guò)程,打印機(jī)的后臺(tái)打印等。用戶界面線程一般用于處理獨(dú)立于其他線程執(zhí)行之外的用戶輸入,響應(yīng)用戶及系統(tǒng)所產(chǎn)生的事件和消息等。但對(duì)于win32的api編程而言,這兩種線程是沒(méi)有區(qū)別的,它們都只需線程的啟動(dòng)地址即可啟動(dòng)線程來(lái)執(zhí)行任務(wù)。
在mfc中,一般用全局函數(shù)afxbeginthread()來(lái)創(chuàng)建并初始化一個(gè)線程的運(yùn)行,該函數(shù)有兩種重載形式,分別用于創(chuàng)建工作者線程和用戶界面線程。兩種重載函數(shù)原型和參數(shù)分別說(shuō)明如下:
(1)cwinthread* afxbeginthread(afx_threadproc pfnthreadproc,lpvoid pparam,npriority=thread_priority_normal,uint nstacksize=0,dword dwcreateflags=0,lpsecurity_attributes lpsecurityattrs=null);
pfnthreadproc:指向工作者線程的執(zhí)行函數(shù)的指針,線程函數(shù)原型必須聲明如下: uint executingfunction(lpvoid pparam);請(qǐng)注意,executingfunction()應(yīng)返回一個(gè)uint類型的值,用以指明該函數(shù)結(jié)束的原因。一般情況下,返回0表明執(zhí)行成功。
pparam:傳遞給線程函數(shù)的一個(gè)32位參數(shù),執(zhí)行函數(shù)將用某種方式解釋該值。它可以是數(shù)值,或是指向一個(gè)結(jié)構(gòu)的指針,甚至可以被忽略;
npriority:線程的優(yōu)先級(jí)。如果為0,則線程與其父線程具有相同的優(yōu)先級(jí);
nstacksize:線程為自己分配堆棧的大小,其單位為字節(jié)。如果nstacksize被設(shè)為0,則線程的堆棧被設(shè)置成與父線程堆棧相同大??; dwcreateflags:如果為0,則線程在創(chuàng)建后立刻開(kāi)始執(zhí)行。如果為create_suspend,則線程在創(chuàng)建后立刻被掛起;
lpsecurityattrs:線程的安全屬性指針,一般為null;
(2)cwinthread* afxbeginthread(cruntimeclass* pthreadclass,int npriority=thread_priority_normal,uint nstacksize=0,dword dwcreateflags=0,lpsecurity_attributes lpsecurityattrs=null);
pthreadclass 是指向 cwinthread 的一個(gè)導(dǎo)出類的運(yùn)行時(shí)類對(duì)象的指針,該導(dǎo)出類定義了被創(chuàng)建的用戶界面線程的啟動(dòng)、退出等;其它參數(shù)的意義同形式1。使用函數(shù)的這個(gè)原型生成的線程也有消息機(jī)制,在以后的例子中我們將發(fā)現(xiàn)同主線程的機(jī)制幾乎一樣。下面對(duì)cwinthread類的數(shù)據(jù)成員及常用函數(shù)進(jìn)行簡(jiǎn)要說(shuō)明。
? ? ? m_hthread:當(dāng)前線程的句柄;
m_nthreadid:當(dāng)前線程的id;
m_pmainwnd:指向應(yīng)用程序主窗口的指針
virtual bool cwinthread::initinstance();重載該函數(shù)以控制用戶界面線程實(shí)例的初始化。初始化成功則返回非0值,否則返回0。用戶界面線程經(jīng)常重載該函數(shù),工作者線程一般不使用initinstance()。
virtual int cwinthread::exitinstance();在線程終結(jié)前重載該函數(shù)進(jìn)行一些必要的清理工作。該函數(shù)返回線程的退出碼,0表示執(zhí)行成功,非0值用來(lái)標(biāo)識(shí)各種錯(cuò)誤。同initinstance()成員函數(shù)一樣,該函數(shù)也只適用于用戶界面線程。
2.2 mfc多線程編程實(shí)例
例程5 multithread5 為了與win32 api對(duì)照,使用mfc 類庫(kù)編程實(shí)現(xiàn)例程3 multithread3。
例程6 multithread6[用戶界面線程] ? 創(chuàng)建用戶界面線程的步驟:
1.使用classwizard創(chuàng)建類cwinthread的派生類(以cuithread類為例)class cuithread : public cwinthread { declare_dyncreate(cuithread)protected: cuithread();// protected constructor used by dynamic creation
// attributes public: // operations public:
// overrides // classwizard generated virtual function overrides //{{afx_virtual(cuithread)public: virtual bool initinstance();virtual int exitinstance();//}}afx_virtual // implementation protected: virtual ~cuithread();// generated message map functions //{{afx_msg(cuithread)
// note-the classwizard will add and remove member functions here.//}}afx_msg
declare_message_map()};
2.重載函數(shù)initinstance()和exitinstance()。bool cuithread::initinstance(){ cframewnd* wnd=new cframewnd;wnd->create(null,“ui thread window”);wnd->showwindow(sw_show);wnd->updatewindow();m_pmainwnd=wnd;return true;}
3.創(chuàng)建新的用戶界面線程 void cuithreaddlg::onbutton1(){
}
請(qǐng)注意以下兩點(diǎn):
a、的開(kāi)頭加入語(yǔ)句: #include “uithread.h” b、把uithread.h中類cuithread()的構(gòu)造函數(shù)的特性由 protected 改為 public。cuithread* pthread=new cuithread();pthread->createthread();
用戶界面線程的執(zhí)行次序與應(yīng)用程序主線程相同,首先調(diào)用用戶界面線程類的initinstance()函數(shù),如果返回true,繼續(xù)調(diào)用線程的run()函數(shù),該函數(shù)的作用是運(yùn)行一個(gè)標(biāo)準(zhǔn)的消息循環(huán),并且當(dāng)收到wm_quit消息后中斷,在消息循環(huán)過(guò)程中,run()函數(shù)檢測(cè)到線程空閑時(shí)(沒(méi)有消息),也將調(diào)用onidle()函數(shù),最后run()函數(shù)返回,mfc調(diào)用exitinstance()函數(shù)清理資源。
你可以創(chuàng)建一個(gè)沒(méi)有界面而有消息循環(huán)的線程,例如:你可以從cwinthread派生一個(gè)新類,在initinstance函數(shù)中完成某項(xiàng)任務(wù)并返回false,這表示僅執(zhí)行initinstance函數(shù)中的任務(wù)而不執(zhí)行消息循環(huán),你可以通過(guò)這種方法,完成一個(gè)工作者線程的功能。
三、線程間通訊
3.1通訊方式
一般而言,應(yīng)用程序中的一個(gè)次要線程總是為主線程執(zhí)行特定的任務(wù),這樣,主線程和次要線程間必定有一個(gè)信息傳遞的渠道,也就是主線程和次要線程間要進(jìn)行通信。這種線程間的通信不但是難以避免的,而且在多線程編程中也是復(fù)雜和頻繁的,下面將進(jìn)行說(shuō)明。
3.1.1使用全局變量進(jìn)行通信
由于屬于同一個(gè)進(jìn)程的各個(gè)線程共享操作系統(tǒng)分配該進(jìn)程的資源,故解決線程間通信最簡(jiǎn)單的一種方法是使用全局變量。對(duì)于標(biāo)準(zhǔn)類型的全局變量,建議使用volatile 修飾符,它告訴編譯器無(wú)需對(duì)該變量作任何的優(yōu)化,即無(wú)需將它放到一個(gè)寄存器中,并且該值可被外部改變。如果線程間所需傳遞的信息較復(fù)雜,可以定義一個(gè)結(jié)構(gòu),通過(guò)傳遞指向該結(jié)構(gòu)的指針進(jìn)行傳遞信息。
3.1.2使用自定義消息
可以在一個(gè)線程的執(zhí)行函數(shù)中向另一個(gè)線程發(fā)送自定義的消息來(lái)達(dá)到通信的目的。一個(gè)線程向另外一個(gè)線程發(fā)送消息是通過(guò)操作系統(tǒng)實(shí)現(xiàn)的。利用windows操作系統(tǒng)的消息驅(qū)動(dòng)機(jī)制,當(dāng)一個(gè)線程發(fā)出一條消息時(shí),操作系統(tǒng)首先接收到該消息,然后把該消息轉(zhuǎn)發(fā)給目標(biāo)線程,接收消息的線程必須已經(jīng)建立了消息循環(huán)。
3.2例程
例程globalobjecttest 該例程演示了如何利用全局變量進(jìn)行通信
例程7[multithread7] 該例程演示了如何使用自定義消息進(jìn)行線程間通信。首先,主線程向ccalculatethread線程發(fā)送消息wm_calculate,ccalculatethread線程收到消息后進(jìn)行計(jì)算,再向主線程發(fā)送wm_display消息,主線程收到該消息后顯示計(jì)算結(jié)果。步驟:
四、線程的同步
4.1基本概念
雖然多線程能給我們帶來(lái)好處,但是也有不少問(wèn)題需要解決。例如,對(duì)于像磁盤(pán)驅(qū)動(dòng)器這樣獨(dú)占性系統(tǒng)資源,由于線程可以執(zhí)行進(jìn)程的任何代碼段,且線程的運(yùn)行是由系統(tǒng)調(diào)度自動(dòng)完成的,具有一定的不確定性,因此就有可能出現(xiàn)兩個(gè)線程同時(shí)對(duì)磁盤(pán)驅(qū)動(dòng)器進(jìn)行操作,從而出現(xiàn)操作錯(cuò)誤;又例如,對(duì)于銀行系統(tǒng)的計(jì)算機(jī)來(lái)說(shuō),可能使用一個(gè)線程來(lái)更新其用戶數(shù)據(jù)庫(kù),而用另外一個(gè)線程來(lái)讀取數(shù)據(jù)庫(kù)以響應(yīng)儲(chǔ)戶的需要,極有可能讀數(shù)據(jù)庫(kù)的線程讀取的是未完全更新的數(shù)據(jù)庫(kù),因?yàn)榭赡茉谧x的時(shí)候只有一部分?jǐn)?shù)據(jù)被更新過(guò)。
使隸屬于同一進(jìn)程的各線程協(xié)調(diào)一致地工作稱為線程的同步。mfc提供了多種同步對(duì)象,下面只介紹最常用的四種:
臨界區(qū)(ccriticalsection)
事件(cevent)
互斥量(cmutex)
信號(hào)量(csemaphore)
通過(guò)這些類,可以比較容易地做到線程同步。
4.2使用 ccriticalsection 類
當(dāng)多個(gè)線程訪問(wèn)一個(gè)獨(dú)占性共享資源時(shí),可以使用“臨界區(qū)”對(duì)象。任一時(shí)刻只有一個(gè)線程可以擁有臨界區(qū)對(duì)象,擁有臨界區(qū)的線程可以訪問(wèn)被保護(hù)起來(lái)的資源或代碼段,其他希望進(jìn)入臨界區(qū)的線程將被掛起等待,直到擁有臨界區(qū)的線程放棄臨界區(qū)時(shí)為止,這樣就保證了不會(huì)在同一時(shí)刻出現(xiàn)多個(gè)線程訪問(wèn)共享資源。
ccriticalsection類的用法非常簡(jiǎn)單,步驟如下:
1.定義ccriticalsection類的一個(gè)全局對(duì)象(以使各個(gè)線程均能訪問(wèn)),如ccriticalsection critical_section;
2.在訪問(wèn)需要保護(hù)的資源或代碼之前,調(diào)用ccriticalsection類的成員lock()獲得臨界區(qū)對(duì)象: ();3.在線程中調(diào)用該函數(shù)來(lái)使線程獲得它所請(qǐng)求的臨界區(qū)。如果此時(shí)沒(méi)有其它線程占有臨界區(qū)對(duì)象,則調(diào)用lock()的線程獲得臨界區(qū);否則,線程將被掛起,并放入到一個(gè)系統(tǒng)隊(duì)列中等待,直到當(dāng)前擁有臨界區(qū)的線程釋放了臨界區(qū)時(shí)為止。
4.訪問(wèn)臨界區(qū)完畢后,使用ccriticalsection的成員函數(shù)unlock()來(lái)釋放臨界區(qū):();通俗講,();語(yǔ)句時(shí),如果其它線程(b)();();語(yǔ)句前的語(yǔ)句時(shí),線程a就會(huì)等待,直到線程b執(zhí)行完();語(yǔ)句,線程a才會(huì)繼續(xù)執(zhí)行。
例程8 multithread8 4.3使用 cevent 類
cevent 類提供了對(duì)事件的支持。事件是一個(gè)允許一個(gè)線程在某種情況發(fā)生時(shí),喚醒另外一個(gè)線程的同步對(duì)象。例如在某些網(wǎng)絡(luò)應(yīng)用程序中,一個(gè)線程(記為a)負(fù)責(zé)監(jiān)聽(tīng)通訊端口,另外一個(gè)線程(記為b)負(fù)責(zé)更新用戶數(shù)據(jù)。通過(guò)使用cevent 類,線程a可以通知線程b何時(shí)更新用戶數(shù)據(jù)。每一個(gè)cevent 對(duì)象可以有兩種狀態(tài):有信號(hào)狀態(tài)和無(wú)信號(hào)狀態(tài)。線程監(jiān)視位于其中的cevent 類對(duì)象的狀態(tài),并在相應(yīng)的時(shí)候采取相應(yīng)的操作。
在mfc中,cevent 類對(duì)象有兩種類型:人工事件和自動(dòng)事件。一個(gè)自動(dòng)cevent 對(duì)象在被至少一個(gè)線程釋放后會(huì)自動(dòng)返回到無(wú)信號(hào)狀態(tài);而人工事件對(duì)象獲得信號(hào)后,釋放可利用線程,但直到調(diào)用成員函數(shù)resetevent()才將其設(shè)置為無(wú)信號(hào)狀態(tài)。在創(chuàng)建cevent 類的對(duì)象時(shí),默認(rèn)創(chuàng)建的是自動(dòng)事件。cevent 類的各成員函數(shù)的原型和參數(shù)說(shuō)明如下:
1、cevent(bool binitiallyown=false,bool bmanualreset=false,lpctstr lpszname=null,lpsecurity_attributes lpsaattribute=null);binitiallyown:指定事件對(duì)象初始化狀態(tài),true為有信號(hào),false為無(wú)信號(hào);
bmanualreset:指定要?jiǎng)?chuàng)建的事件是屬于人工事件還是自動(dòng)事件。true為人工事件,false為自動(dòng)事件;
后兩個(gè)參數(shù)一般設(shè)為null,在此不作過(guò)多說(shuō)明。
2、bool cevent::setevent();
將 cevent 類對(duì)象的狀態(tài)設(shè)置為有信號(hào)狀態(tài)。如果事件是人工事件,則 cevent 類對(duì)象保持為有信號(hào)狀態(tài),直到調(diào)用成員函數(shù)resetevent()將 其重新設(shè)為無(wú)信號(hào)狀態(tài)時(shí)為止。如果cevent 類對(duì)象為自動(dòng)事件,則在setevent()將事件設(shè)置為有信號(hào)狀態(tài)后,cevent 類對(duì)象由系統(tǒng)自動(dòng)重置為無(wú)信號(hào)狀態(tài)。
如果該函數(shù)執(zhí)行成功,則返回非零值,否則返回零。
3、bool cevent::resetevent();
該函數(shù)將事件的狀態(tài)設(shè)置為無(wú)信號(hào)狀態(tài),并保持該狀態(tài)直至setevent()被調(diào)用時(shí)為止。由于自動(dòng)事件是由系統(tǒng)自動(dòng)重置,故自動(dòng)事件不需要調(diào)用該函數(shù)。如果該函數(shù)執(zhí)行成功,返回非零值,否則返回零。一般通過(guò)調(diào)用waitforsingleobject函數(shù)來(lái)監(jiān)視事件狀態(tài)。前面已經(jīng)介紹了該函數(shù)。由于語(yǔ)言描述的原因,cevent 類的理解確實(shí)有些難度,只要通過(guò)下面例程,多看幾遍就可理解。例程9 multithread9 仔細(xì)分析這兩個(gè)線程函數(shù), 就會(huì)正確理解cevent 類。線程writed執(zhí)行到 waitforsingleobject(eventwrited.m_hobject,infinite);處等待,直到事件eventwrited為有信號(hào)該線程才往下執(zhí)行,因?yàn)閑ventwrited對(duì)象是自動(dòng)事件,則當(dāng)waitforsingleobject()返回時(shí),系統(tǒng)自動(dòng)把eventwrited對(duì)象重置為無(wú)信號(hào)狀態(tài)。
4.4使用cmutex 類
互斥對(duì)象與臨界區(qū)對(duì)象很像.互斥對(duì)象與臨界區(qū)對(duì)象的不同在于:互斥對(duì)象可以在進(jìn)程間使用,而臨界區(qū)對(duì)象只能在同一進(jìn)程的各線程間使用。當(dāng)然,互斥對(duì)象也可以用于同一進(jìn)程的各個(gè)線程間,但是在這種情況下,使用臨界區(qū)會(huì)更節(jié)省系統(tǒng)資源,更有效率。
4.5使用csemaphore 類
當(dāng)需要一個(gè)計(jì)數(shù)器來(lái)限制可以使用某個(gè)線程的數(shù)目時(shí),可以使用“信號(hào)量”對(duì)象。csemaphore 類的對(duì)象保存了對(duì)當(dāng)前訪問(wèn)某一指定資源的線程的計(jì)數(shù)值,該計(jì)數(shù)值是當(dāng)前還可以使用該資源的線程的數(shù)目。如果這個(gè)計(jì)數(shù)達(dá)到了零,則所有對(duì)這個(gè)csemaphore 類對(duì)象所控制的資源的訪問(wèn)嘗試都被放入到一個(gè)隊(duì)列中等待,直到超時(shí)或計(jì)數(shù)值不為零時(shí)為止。一個(gè)線程被釋放已訪問(wèn)了被保護(hù)的資源時(shí),計(jì)數(shù)值減1;一個(gè)線程完成了對(duì)被控共享資源的訪問(wèn)時(shí),計(jì)數(shù)值增1。這個(gè)被csemaphore 類對(duì)象所控制的資源可以同時(shí)接受訪問(wèn)的最大線程數(shù)在該對(duì)象的構(gòu)建函數(shù)中指定。
csemaphore 類的構(gòu)造函數(shù)原型及參數(shù)說(shuō)明如下:
csemaphore(long linitialcount=1,long lmaxcount=1,lpctstr pstrname=null,lpsecurity_attributes lpsaattributes=null);linitialcount:信號(hào)量對(duì)象的初始計(jì)數(shù)值,即可訪問(wèn)線程數(shù)目的初始值;
lmaxcount:信號(hào)量對(duì)象計(jì)數(shù)值的最大值,該參數(shù)決定了同一時(shí)刻可訪問(wèn)由信號(hào)量保護(hù)的資源的線程最大數(shù)目;
后兩個(gè)參數(shù)在同一進(jìn)程中使用一般為null,不作過(guò)多討論;
在用csemaphore 類的構(gòu)造函數(shù)創(chuàng)建信號(hào)量對(duì)象時(shí)要同時(shí)指出允許的最大資源計(jì)數(shù)和當(dāng)前可用資源計(jì)數(shù)。一般是將當(dāng)前可用資源計(jì)數(shù)設(shè)置為最大資源計(jì)數(shù),每增加一個(gè)線程對(duì)共享資源的訪問(wèn),當(dāng)前可用資源計(jì)數(shù)就會(huì)減1,只要當(dāng)前可用資源計(jì)數(shù)是大于0的,就可以發(fā)出信號(hào)量信號(hào)。但是當(dāng)前可用計(jì)數(shù)減小到0時(shí),則說(shuō)明當(dāng)前占用資源的線程數(shù)已經(jīng)達(dá)到了所允許的最大數(shù)目,不能再允許其它線程的進(jìn)入,此時(shí)的信號(hào)量信號(hào)將無(wú)法發(fā)出。線程在處理完共享資源后,應(yīng)在離開(kāi)的同時(shí)通過(guò)releasesemaphore()函數(shù)將當(dāng)前可用資源數(shù)加1。例程10 multithread10 為了文件中能夠正確使用同步類,在文件開(kāi)頭添加: #include “afxmt.h” 定義信號(hào)量對(duì)象和一個(gè)字符數(shù)組,為了能夠在不同線程間使用,定義為全局變量:csemaphore semaphorewrite(2,2);//資源最多訪問(wèn)線程2個(gè),當(dāng)前可訪問(wèn)線程數(shù)2個(gè)
在信號(hào)量對(duì)象有信號(hào)的狀態(tài)下,線程執(zhí)行到waitforsingleobject語(yǔ)句處繼續(xù)執(zhí)行,同時(shí)可用線程數(shù)減1;若線程執(zhí)行到waitforsingleobject語(yǔ)句時(shí)信號(hào)量對(duì)象無(wú)信號(hào),線程就在這里等待,直到信號(hào)量對(duì)象有信號(hào)線程才往下執(zhí)行。
java多線程知識(shí)點(diǎn)總結(jié)篇四
in the following code, which is the earliest statement, where the object originally held in e, may be garbage collected:
class test {
static void main(string args []){
ee e = new employee(“bob”, 48);
atepay();
n(etails());
6.e = null;
7.e = new employee(“denise”, 36);
atepay();
n(etails());
10.}
11.}
only one:
in the following code, which is the earliest statement, where the object originally held in e, may be garbage collected:
class test {
static void main(string args []){
ee e = new employee(“bob”, 48);
atepay();
n(etails());
6.e = null;
7.e = new employee(“denise”, 36);
atepay();
n(etails());
10.}
11.}
only one:
10
11
7
8
2:exhibit :
class test(e static int j = 0;
e static boolean methodb(int k)(5.j += k;
true;
6.)
static void methoda(int i){
n b:
10.b = i < 10 | methodb(4);
11.b = i < 10 || methodb(8);
12.)
static void main(string args[] }(a(0);
n(j);
17.)
18.)
what is the result?
program prints “0”
program prints “4”
program prints “8”
program prints “12”
3:what is written to the standard output given the following statement:n(4|7);
select the right answer:
a.4
b.5
c.6
d.7
4:
select valid identifier of java:
select valid identifier of java:
a.%passwd
b.3d_game
c.$charge
5:設(shè)有變量說(shuō)明語(yǔ)句int a=1,b=0;
則執(zhí)行以下程序段的輸出結(jié)果為()。
switch(a)
{
case 1:
switch(b)
{
case 0:printf(“**0**”);break;
case 1:printf(“**1**”);break;
}
case 2:printf(“**2**”);break;
}
printf(“ ”);
a.**0**
b.**0****2**
c.**0****1****2**
d.有語(yǔ)法錯(cuò)誤
6:in the following pieces of code, which one will compile without any error?
buffer sb1 = “abcd”;
n b = new boolean(“abcd”);
c.c: byte b = 255;
fl = 1.2;
7:
what is the result when you compile and run the following code?
public class throwsdemo
{
static void throwmethod()
{
n(“inside throwmethod.”);
throw new illegalaccessexception(“demo”);
}
public static void main(string args[])
{
try
{
throwmethod();
}
catch(illegalaccessexception e)
{
n(“caught ” + e);
}
}
}
choices:
what is the result when you compile and run the following code?
public class throwsdemo
{
static void throwmethod()
{
n(“inside throwmethod.”);
throw new illegalaccessexception(“demo”);
}
public static void main(string args[])
{
try
{
throwmethod();
}
catch(illegalaccessexception e)
{
n(“caught ” + e);
}
}
}
choices:
ation error
e error
e successfully, nothing is ed by caught:laccessexcption: demo
8:which of the following statements are not legal?
l = 4990;
i = 4l;
d = 34.4;
t = 0.9f.9:
give the following java class:
public class example{
public static void main(string args[]){
static int x[] = new int[15];
n(x[5]);
}
}
which statement is corrected?
give the following java class:
public class example{
public static void main(string args[]){
static int x[] = new int[15];
n(x[5]);
}
}
which statement is corrected?
compile, some error will run, some error will is is null.10:下面關(guān)于變量及其范圍的陳述哪些是錯(cuò)的。
a.實(shí)例變量是類的成員變量。
b.實(shí)例變量用關(guān)鍵字static聲明。
c.在方法中定義的局部變量在該方法被執(zhí)行時(shí)創(chuàng)建
d.局部變量在使用前必須被初始化。
11:
public class x{
public object m(){
object o = new float(3.14f);//line 3
object [] oa = new object[1];//line 4
oa[0] = o;//line 5
o=null;//line 6
return oa[0];//line 7
}
}
when is the float object, created in line 3,eligible for garbage collection?
public class x{
public object m(){
object o = new float(3.14f);//line 3
object [] oa = new object[1];//line 4
oa[0] = o;//line 5
o=null;//line 6
return oa[0];//line 7
}
}
when is the float object, created in line 3,eligible for garbage collection?
after line after line 6
after line 7(that is,as the method returns)
in this method
12:
which is the most appropriate code snippet that can be inserted at line 18 in the following code?
(assume that the code is compiled and run with assertions enabled)
.*;
class asserttest
4.{
e hashmap cctld;
asserttest()
8.{
= new hashmap();
(“in”, “india”);
(“uk”, “united kingdom”);
(“au”, “australia”);
13.// more code...14.}
15.// other methods.... string getcountry(string countrycode)
17.{
18.// what should be inserted here?
country =(string)(countrycode);
country;
21.}
22.}
which is the most appropriate code snippet that can be inserted at line 18 in the following code?
(assume that the code is compiled and run with assertions enabled)
.*;
class asserttest
4.{
e hashmap cctld;
asserttest()
8.{
= new hashmap();
(“in”, “india”);
(“uk”, “united kingdom”);
(“au”, “australia”);
13.// more code...14.}
15.// other methods.... string getcountry(string countrycode)
17.{
18.// what should be inserted here?
country =(string)(countrycode);
country;
21.}
22.}
countrycode!= null;
countrycode!= null : “country code can not be null”;
cctld!= null : “no country code data is available”;
cctld : “no country code data is available”;
13:
give the following code:
public class example{
public static void main(string args[]){
int l=0;
do{
n(“doing it for l is:”+l);
}while(—l>0)
n(“finish”);
}
}
which well be output:
give the following code:
public class example{
public static void main(string args[]){
int l=0;
do{
n(“doing it for l is:”+l);
}while(—l>0)
n(“finish”);
}
}
which well be output:
it for l is 3
it for l is 1
it for l is 2
it for l is 0
14:which statements about java code security are not true?
bytecode verifier loads all classes needed for the execution of a ing code is performed by the runtime runtime the bytecodes are loaded, checked and run in an interpreter. class loader adds security by separating the namespaces for the classes of the local file system from those imported from network sources.15:a class design requires that a member variable should be accessible only by same package, which modifer word should be used?
ted
modifer
e
16:character流與byte流的區(qū)別是
a.每次讀入的字節(jié)數(shù)不同
b.前者帶有緩沖,后者沒(méi)有
c.前者是塊讀寫(xiě),后者是字節(jié)讀寫(xiě)
d.二者沒(méi)有區(qū)別,可以互換使用
簡(jiǎn)答題
17:找出兩個(gè)字符串中最大子字符串,如“abractyeyt”,“dgdsaeactyey”的最大子串為“actyet”
18:假設(shè)你有一個(gè)用1001個(gè)整數(shù)組成的數(shù)組,這些整數(shù)是任意排列的,但是你知道所有的整數(shù)都在1到1000(包括1000)之間。此外,除一個(gè)數(shù)字出現(xiàn)兩次外,其他所有數(shù)字只出現(xiàn)一次。假設(shè)你只能對(duì)這個(gè)數(shù)組做一次處理,用一種算法找出重復(fù)的那個(gè)數(shù)字。如果你在運(yùn)算中使用了輔助的存儲(chǔ)方式,那么你能找到不用這種方式的算法嗎?
19:到底在哪里使用cascade=“...”?
20:使用tomcat部署應(yīng)用程序 emoryerror 嗎?如何解決的。
21:請(qǐng)寫(xiě)一個(gè)java程序?qū)崿F(xiàn)數(shù)據(jù)庫(kù)緩沖池的功能?
22:有200個(gè)正整數(shù),且每個(gè)數(shù)均在1000至9999之間。請(qǐng)編制函數(shù),其函數(shù)的功能是:要求按每個(gè)數(shù)的后三位的大小進(jìn)行升序排列,然后取出滿足此條件的前10個(gè)數(shù)依次存入數(shù)組bb中,如果后三位的數(shù)值相等,則按原先的數(shù)值進(jìn)行降序排列。
23:anonymous inner class(匿名內(nèi)部類)是否可以extends(繼承)其它類,是否可以implements(實(shí)現(xiàn))interface(接口)?
24:找出字符串a(chǎn)中包含的字符可以進(jìn)行的所有不同組合。例如:abccd中,ab,ac,bc,cc,abd等都是可能的組合。
25:下面的代碼在絕大部分時(shí)間內(nèi)都運(yùn)行得很正常,請(qǐng)問(wèn)在什么情況下會(huì)出現(xiàn)問(wèn)題?問(wèn)題的根源在哪里?
import .linkedlist;
public class stack {
linkedlist list = new linkedlist();
public synchronized void push(object x){
synchronized(list){
t(x);
notify();
}
}
public synchronized object pop()
throws exception {
synchronized(list){
if(()<= 0){
wait();
}
return last();
}
}
}
java多線程知識(shí)點(diǎn)總結(jié)篇五
《java編程》
計(jì)算器
班級(jí):****** 姓名:******
學(xué)號(hào): ******* 指導(dǎo)老師:******
實(shí)驗(yàn)名稱:java計(jì)算器
1實(shí)驗(yàn)?zāi)康? java編程語(yǔ)言在編程方面的具體應(yīng)用,以及使用面向?qū)ο蠓椒?對(duì)小應(yīng)用程序進(jìn)行需求分
析、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì),最后使用java編程實(shí)現(xiàn)的全過(guò)程。
2實(shí)驗(yàn)意義:
在編程我們使用的java語(yǔ)言,是目前比較流行的編程語(yǔ)言。在當(dāng)今這個(gè)時(shí)代,java語(yǔ)言在編程方面的優(yōu)勢(shì)使得編程有了更好的選擇。java語(yǔ)言最大的特點(diǎn)是具有跨平臺(tái)性,使其不受平臺(tái)不同的影響,得到了廣泛的應(yīng)用。實(shí)訓(xùn)性質(zhì)
本課程是計(jì)算機(jī)信息管理專業(yè)的一門實(shí)踐性課程,是《java編程》課程的實(shí)踐性教學(xué)環(huán)節(jié)。實(shí)訓(xùn)目標(biāo)
⑴綜合應(yīng)用java程序設(shè)計(jì)的知識(shí)解決實(shí)際問(wèn)題。
⑵學(xué)會(huì)在應(yīng)用程序的設(shè)計(jì)過(guò)程中,應(yīng)用面向?qū)ο蟮某绦蛟O(shè)計(jì)方法。⑶學(xué)會(huì)應(yīng)用jdbc創(chuàng)建數(shù)據(jù)庫(kù)應(yīng)用程序。
⑷學(xué)會(huì)開(kāi)發(fā)基于swing的應(yīng)用程序及多文檔應(yīng)用程序的設(shè)計(jì)。實(shí)訓(xùn)任務(wù)
用java語(yǔ)言開(kāi)發(fā)工具(例如jdk、jcreator、netbeans等)制作一個(gè)簡(jiǎn)單的可運(yùn)行的完整的應(yīng)用程序或小型系統(tǒng),并編制出各階段必要的文檔。
將創(chuàng)建一個(gè)計(jì)算器,可以進(jìn)行常用的加減乘除算術(shù)運(yùn)算。本實(shí)例的知識(shí)點(diǎn)有:窗口布局器gridlayout的應(yīng)用,對(duì)按鈕消息的監(jiān)聽(tīng)和響應(yīng)。
6實(shí)訓(xùn)條件
<軟件:>windows xp,netbeans ide 6.52 7開(kāi)發(fā)背景: java是由sun microsystems公司于1995年5月推出的java程序設(shè)計(jì)語(yǔ)言(以下簡(jiǎn)稱java語(yǔ)言)和java平臺(tái)的總稱。java語(yǔ)言是一個(gè)支持網(wǎng)絡(luò)計(jì)算的面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言。java語(yǔ)言吸收了smalltalk語(yǔ)言和c++語(yǔ)言的優(yōu)點(diǎn),并增加了其它特性,如支持并發(fā)程序設(shè)計(jì)、網(wǎng)絡(luò)通信、和多媒體數(shù)據(jù)控制等。
8系統(tǒng)部分分析:
1)java語(yǔ)言是簡(jiǎn)單的。java語(yǔ)言的語(yǔ)法與c語(yǔ)言和c++語(yǔ)言很接近,使得大多數(shù)程序員很容易學(xué)習(xí)和使用java。另一方面,java丟棄了c++ 中很少使用的、很難理解的、令人迷惑的那些特性,如操作符重載、多繼承、自動(dòng)的強(qiáng)制類型轉(zhuǎn)換。
2)java語(yǔ)言是一個(gè)面向?qū)ο蟮摹ava語(yǔ)言提供類、接口和繼承等原語(yǔ),為了簡(jiǎn)單起見(jiàn),只支持類之間的單繼承,但支持接口之間的多繼承,并支持類與接口之間的實(shí)現(xiàn)機(jī)制(關(guān)鍵字為implements)。java語(yǔ)言全面支持動(dòng)態(tài)綁定,而c++ 語(yǔ)言只對(duì)虛函數(shù)使用動(dòng)態(tài)綁定
3)java語(yǔ)言是分布式的。java語(yǔ)言支持internet應(yīng)用的開(kāi)發(fā),在基本的java應(yīng)用編程接口中有一個(gè)網(wǎng)絡(luò)應(yīng)用編程接口(),它提供了用于網(wǎng)絡(luò)應(yīng)用編程的類庫(kù),包括url、urlconnection、socket、serversocket等。java的rmi(遠(yuǎn)程方法激活)機(jī)制也是開(kāi)發(fā)分布式應(yīng)用的重要手段。
4)java語(yǔ)言是健壯的。java的強(qiáng)類型機(jī)制、異常處理、廢料的自動(dòng)收集等是java程序健壯性的重要保證。對(duì)指針的丟棄是java的明智選擇。java的安全檢查機(jī)制使得java更具健壯性。
5)java語(yǔ)言是安全的。java通常被用在網(wǎng)絡(luò)環(huán)境中,為此,java提供了一個(gè)安全機(jī)制以防惡意代碼的攻擊。除了java語(yǔ)言具有的許多安全特性以外,java對(duì)通過(guò)網(wǎng)絡(luò)下載的類具有一個(gè)安全防范機(jī)制(類classloader),如分配不同的名字空間以防替代本地的同名類、字節(jié)代碼檢查,并提供安全管理機(jī)制.6)java語(yǔ)言是體系結(jié)構(gòu)中立的。java程序(后綴為java的文件)在java平臺(tái)上被編譯為體系結(jié)構(gòu)中立的字節(jié)碼格式(后綴為class的文件), 然后可以在實(shí)現(xiàn)這個(gè)java平臺(tái)的任何系統(tǒng)中運(yùn)行。
7)java語(yǔ)言是可移植的。這種可移植性來(lái)源于體系結(jié)構(gòu)中立性,另外,java還嚴(yán)格規(guī)定了各個(gè)基本數(shù)據(jù)類型的長(zhǎng)度。java系統(tǒng)本身也具有很強(qiáng)的可移植性,java編譯器是用java實(shí)現(xiàn)的.8)java語(yǔ)言是解釋型的。如前所述,java程序在java平臺(tái)上被編譯為字節(jié)碼格式,然后可以在實(shí)現(xiàn)這個(gè)java平臺(tái)的任何系統(tǒng)中運(yùn)行。
9)java是高性能的。與那些解釋型的高級(jí)腳本語(yǔ)言相比,java的確是高性能的。事實(shí)上,java的運(yùn)行速度隨著jit(just-in-time)編譯器技術(shù)的發(fā)展越來(lái)越接近于c++。
10)java語(yǔ)言是多線程的。在java語(yǔ)言中,線程是一種特殊的對(duì)象,它必須由thread類或其子(孫)類來(lái)創(chuàng)建。
11)java語(yǔ)言是動(dòng)態(tài)的。java語(yǔ)言的設(shè)計(jì)目標(biāo)之一是適應(yīng)于動(dòng)態(tài)變化的環(huán)境。
目錄
課程設(shè)計(jì)題目 ……………………………… p1
課程設(shè)計(jì)簡(jiǎn)介 ……………………………… p2
課程設(shè)計(jì)源代碼…………………………… p5
課程設(shè)計(jì)運(yùn)行結(jié)果 ……………………… p15 課程設(shè)計(jì)心得體會(huì) ………………………
p16
package computerpad;import .*;import .event.*;import .*;import .*;import list;import format;public class computerpad extends frame implements actionlistener {
numberbutton numberbutton[];
operationbutton oprationbutton[];
button 小數(shù)點(diǎn)按鈕,正負(fù)號(hào)按鈕,退格按鈕,求倒數(shù)按鈕,等號(hào)按鈕,清零按鈕;
panel panel;
jtextfield resultshow;
string 運(yùn)算符號(hào)[]={“+”,“-”,“*”,“/”};
linkedlist 鏈表;
boolean 是否按下等號(hào)=false;
public computerpad()
{
super(“計(jì)算器”);
鏈表=new linkedlist();
numberbutton=new numberbutton[10];
for(int i=0;i<=9;i++)
{
numberbutton[i]=new numberbutton(i);
numberbutton[i].addactionlistener(this);
}
oprationbutton=new operationbutton[4];
for(int i=0;i<4;i++)
{
oprationbutton[i]=new operationbutton(運(yùn)算符號(hào)[i]);
oprationbutton[i].addactionlistener(this);
}
小數(shù)點(diǎn)按鈕=new button(“.”);
正負(fù)號(hào)按鈕
=new button(“+/-”);
等號(hào)按鈕=new button(“=”);
求倒數(shù)按鈕=new button(“1/x”);
退格按鈕=new button(“退格”);
清零按鈕=new button(“c”);
eground();
eground();
eground();
eground();
eground();
eground();
ionlistener(this);
ionlistener(this);
ionlistener(this);
ionlistener(this);
ionlistener(this);
ionlistener(this);
resultshow=new jtextfield(10);
izontalalignment();
eground();
t(new font(“timesroman”,,14));
der(new softbevelborder(d));
kground();
table(false);
panel=new panel();
out(new gridlayout(4,5));
(numberbutton[1]);
(numberbutton[2]);
(numberbutton[3]);
(oprationbutton[0]);
(清零按鈕);
(numberbutton[4]);
(numberbutton[5]);
(numberbutton[6]);
(oprationbutton[1]);
(退格按鈕);
(numberbutton[7]);
(numberbutton[8]);
(numberbutton[9]);
(oprationbutton[2]);
(求倒數(shù)按鈕);
(numberbutton[0]);
(正負(fù)號(hào)按鈕);
(小數(shù)點(diǎn)按鈕);
(oprationbutton[3]);
(等號(hào)按鈕);
add(panel,);
add(resultshow,);
addwindowlistener(new windowadapter()
{ public void windowclosing(windowevent e)
{
(0);
}
});
setvisible(true);
setbounds(100,50,240,180);
setresizable(false);
validate();
} public void actionperformed(actionevent e)
{
if(rce()instanceof numberbutton)
{
numberbutton b=(numberbutton)rce();
if(()==0)
{
int number=ber();
(“"+number);
t(”“+number);
是否按下等號(hào)=false;
}
else if(()==1&&是否按下等號(hào)==false)
{
int number=ber();
string num=(string)first();
string s=(”“+number);
(0,s);
t(s);
}
else if(()==1&&是否按下等號(hào)==true)
{
int number=ber();
first();
(”“+number);
是否按下等號(hào)=false;
t(”“+number);
}
else if(()==2)
{
int number=ber();
(”“+number);
t(”“+number);
}
else if(()==3)
{
int number=ber();
string num=(string)t();
string s=(”“+number);
(2,s);
t(s);
}
}
else if(rce()instanceof operationbutton)
{
operationbutton b=(operationbutton)rce();
if(()==1)
{
string fuhao=運(yùn)算符號(hào)();
(fuhao);
}
else if(()==2)
{
string fuhao=運(yùn)算符號(hào)();
(1,fuhao);
}
else if(()==3)
{
string fuhao=運(yùn)算符號(hào)();
string number1=(string)first();
string number2=(string)t();
string 運(yùn)算符號(hào)=(string)(1);
try
{
double n1=ouble(number1);
double n2=ouble(number2);
double n=0;
if((”+“))
{
n=n1+n2;
}
else if((”-“))
{
n=n1-n2;
}
else if((”*“))
{
n=n1*n2;
}
else if((”/“))
{
n=n1/n2;
}
();
(”“+n);
(fuhao);
t(”“+n);
}
catch(exception ee)
{
}
}
}
else if(rce()==等號(hào)按鈕)
{
是否按下等號(hào)=true;
if(()==1||()==2)
{
string num=(string)first();
t(”“+num);
}
else if(()==3)
{
string number1=(string)first();
string number2=(string)t();
string 運(yùn)算符號(hào)=(string)(1);
try
{
double n1=ouble(number1);
double n2=ouble(number2);
double n=0;
if((”+“))
{
n=n1+n2;
}
else if((”-“))
{
n=n1-n2;
}
else if((”*“))
{
n=n1*n2;
}
else if((”/“))
{
n=n1/n2;
}
t(”“+n);
(0,”“+n);
last();
last();
}
catch(exception ee)
{
}
}
}
else if(rce()==小數(shù)點(diǎn)按鈕)
{
if(()==0)
{
是否按下等號(hào)=false;
}
else if(()==1)
{
string dot=el();
string num=(string)first();
string s=null;
if(f(dot)==-1)
{
s=(dot);
(0,s);
}
else
{
s=num;
}
(0,s);
t(s);
}
else if(()==3)
{
string dot=el();
string num=(string)t();
string s=null;
if(f(dot)==-1)
{
s=(dot);
(2,s);
}
else
{
s=num;
}
t(s);
}
}
else if(rce()==退格按鈕)
{
if(()==1)
{
string num=(string)first();
if(()>=1)
{
num=ing(0,()-1);
(0,num);
t(num);
}
else
{
last();
t(”0“);
}
}
else if(()==3)
{
string num=(string)t();
if(()>=1)
{ num=ing(0,()-1);
(2,num);
t(num);
}
else
{
last();
t(”0“);
}
}
}
else if(rce()==正負(fù)號(hào)按鈕)
{
if(()==1)
{
string number1=(string)first();
try
{
double d=ouble(number1);
d=-1*d;
string str=f(d);
(0,str);
t(str);
}
catch(exception ee)
{
}
}
else if(()==3)
{
string number2=(string)t();
try
{
double d=ouble(number2);
d=-1*d;
string str=f(d);
(2,str);
t(str);
}
catch(exception ee){
}
}
}
else if(rce()==求倒數(shù)按鈕)
{
if(()==1||()==2)
{
string number1=(string)first();
try
{
double d=ouble(number1);
d=1.0/d;
string str=f(d);
(0,str);
t(str);
}
catch(exception ee){
}
}
else if(()==3)
{
string number2=(string)t();
try
{
double d=ouble(number2);
d=1.0/d;
string str=f(d);
(0,str);
t(str);
}
catch(exception ee){
}
}
}
else if(rce()==清零按鈕)
{
是否按下等號(hào)=false;
t(”0“);
();
}
} public static void main(string args[])
{
new computerpad();
}
}
package computerpad;import .*;import .event.*;import .*;public class numberbutton extends button {
int number;
public numberbutton(int number)
{
super(”"+number);
=number;
setforeground();
}
public int getnumber()
{
return number;
} }
import .*;import .event.*;import .*;public class operationbutton extends button {
string 運(yùn)算符號(hào);
public operationbutton(string s)
{
super(s);
運(yùn)算符號(hào)=s;
setforeground();
}
public string get運(yùn)算符號(hào)()
{
return 運(yùn)算符號(hào);
} } 14 java實(shí)訓(xùn)心得:
未接觸java之前,聽(tīng)人說(shuō)java這門語(yǔ)言如何的強(qiáng)大和難以入門,但學(xué)習(xí)之后,給我的感覺(jué)卻是語(yǔ)言沒(méi)有所謂的難于不難,關(guān)鍵是自己有沒(méi)有真正投入去學(xué),有沒(méi)有花時(shí)間去學(xué)。java是一門很好的語(yǔ)言,經(jīng)過(guò)周圍人對(duì)java的宣傳,我一開(kāi)始不敢去學(xué)習(xí)這門語(yǔ)言,因?yàn)橐婚T高級(jí)語(yǔ)言總是讓人想到一開(kāi)始的學(xué)習(xí)會(huì)很難,但是后來(lái)在自己的努力和老師同學(xué)的幫助下,我加入了java學(xué)習(xí)者的行列。
老師把我們帶進(jìn)了門,那么,以后漫長(zhǎng)的深入學(xué)習(xí)還是要靠自己。經(jīng)常性的編寫(xiě)一些程序,或則去看懂、研究透別人編寫(xiě)的程序?qū)τ谖覀兇蚝没A(chǔ)是非常有利的。讓我們懷著對(duì)java的一腔熱情,用自己的刻苦努力去把java學(xué)好。將來(lái),用自己的成績(jī)?nèi)セ貓?bào)有恩于我們的社會(huì)、家人和朋友。
【本文地址:http://mlvmservice.com/zuowen/1082481.html】