鄭逸凡
(福州外語外貿(mào)學(xué)院,福建 福州 350202)
在操作系統(tǒng)中,進程是程序的一次執(zhí)行,比如當(dāng)雙擊某個可執(zhí)行文件后,系統(tǒng)就創(chuàng)建一個進程專門執(zhí)行這個程序的代碼,在執(zhí)行過程中,進程會申請、持有或釋放操作系統(tǒng)資源(文件、內(nèi)存等).在操作系統(tǒng)發(fā)展早期,進程是資源分配、調(diào)度、執(zhí)行的基本單位,但由于進程持有系統(tǒng)資源等,調(diào)度時系統(tǒng)開銷很大,于是便出現(xiàn)了輕量級進程——線程.
一個進程可擁有多個線程,這些線程共享此進程所持有的系統(tǒng)資源.現(xiàn)代操作系統(tǒng)中,調(diào)度、執(zhí)行的基本單位變成了線程,進程則還是資源分配的基本單位.由于線程本身幾乎不持有系統(tǒng)資源,在調(diào)度時系統(tǒng)開銷就很小.操作系統(tǒng)可以擁有多個進程,感覺就像多個程序同時在執(zhí)行;進程可以擁有多個線程,感覺就像一個程序可以同時做多件事情.
多線程編程是指讓程序使用多個線程同時分別做一件事情的不同部分,或者同時做不同的事情,但并不是所有的事情都適合多線程,多線程編程的目的是提高程序執(zhí)行效率、提高人們的工作效率.
在Java中,Thread類是所有線程類的超類,開發(fā)人員可以編寫一個類繼承Thread,并重寫run方法,在run方法里面編寫線程將要執(zhí)行的代碼.創(chuàng)建線程對象后,只需要調(diào)用start()方法即可讓線程進入就緒隊列,等待操作系統(tǒng)調(diào)度.需要特別注意的是調(diào)度具有隨機性和隨時性,也就是說無法確定下一次調(diào)度哪個線程,也無法確定什么時刻進行調(diào)度.在Java中,繼承Thread類創(chuàng)建線程的代碼如下:
public class ThreadTest{
public static void main(String[]args){
MyThread myThread=new MyThread();
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run(){
System.out.println("自己創(chuàng)建的線程執(zhí)行了");
}
}
除了繼承Thread類重寫run方法外,在簡單的情況下,還可通過實現(xiàn)Runnable接口的方式編寫線程執(zhí)行的代碼,具體實現(xiàn)代碼如下:
Thread thread=new Thread(new Runnable(){
@Override
public void run(){
System.out.println("Runnable接口方式實現(xiàn)多線程");
}
});
一個數(shù)據(jù),如一個對象或?qū)ο笾械哪硞€字段,如果有多個線程可以同時訪問它,就可能會出現(xiàn)線程安全問題:數(shù)據(jù)錯亂、程序出錯或其他無法預(yù)知的問題.比如線程1要遍歷一個list集合,線程2要把這個list集合清空,如果這兩個線程同時執(zhí)行就可能會出現(xiàn)線程安全問題.線程同步控制,即使用某種方式使得一個線程在操作完某個數(shù)據(jù)前,別的線程無法操作這個數(shù)據(jù),從而避免多個線程同時操作一個數(shù)據(jù),進而避免線程安全問題.
在Java中每個對象都有一把鎖,同一時刻只能有一個線程持有這把鎖,線程可以使用synchronized關(guān)鍵字向系統(tǒng)申請某個對象的鎖,得到鎖之后,別的線程再申請該鎖時,就只能等待.持有鎖的線程在這次操作完成后,可以釋放鎖,以便其他線程可以獲得鎖.例如,以synchronized代碼塊實現(xiàn)同步鎖機制的主要代碼如下:
Thread thread1=new Thread(new Runnable(){
@Override
public void run(){
synchronized(list){
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
});
Thread thread2=new Thread(new Runnable(){
@Override
public void run(){
synchronized(list){
list.clear();
}
}
});
對于稍復(fù)雜的情況,比如多個線程需要相互合作有規(guī)律的訪問共享數(shù)據(jù),就可以使用wait/notify機制,即等待/通知機制,也稱等待/喚醒機制.
等待/通知機制建立在synchronized同步鎖機制的基礎(chǔ)上,即在同步代碼塊(或同步方法)內(nèi),如果當(dāng)前線程執(zhí)行了lockObject.wait()(lockObject表示提供鎖的對象),則當(dāng)前線程立即暫停執(zhí)行,并被放入阻塞隊列,并向系統(tǒng)歸還所持有的鎖,并在lockObject上等待,直到別的線程調(diào)用lockObject.notify().如果有多個線程在同一個對象上等待,notify()方法只會隨機通知一個等待的線程,也可以使用notifyAll()方法通知所有等待的線程.被通知的線程獲得鎖后會進入就緒隊列.
假設(shè)線程1需要同時擁有資源A和資源B才能工作,線程2需要同時擁有資源A和資源B才能工作,在進行同步控制時有可能出現(xiàn)這種情況:線程1擁有資源A,線程2擁有資源B,兩個線程相互等待對方先釋放資源,并會一直這么僵持下去,這種情況稱為死鎖.
為了避免死鎖,可以使用信號量機制:線程在嘗試申請某個資源前都要判斷能否一次性就獲得所有需要的資源,如果能,就申請,如果不能,則不申請,一直等到可以一次性獲得所有資源.
網(wǎng)絡(luò)編程,主要是指基于TCP的網(wǎng)絡(luò)通信編程,在Java中網(wǎng)絡(luò)編程是使用Socket類實現(xiàn),因此也稱為socket編程.socket編程模型中有服務(wù)器端和客戶端,服務(wù)器端使用ServerSocket創(chuàng)建,一般有固定的IP地址和端口號,方便向外界提供服務(wù).客戶端可以有多個,并且使用Socket主動連接服務(wù)器.連接后,服務(wù)器端也創(chuàng)建一個Socket對象表示這次連接.
在Java中實現(xiàn)socket編程,服務(wù)器端要做的事情主要有:創(chuàng)建服務(wù)器對象ServerSocket;等待客戶端的連接請求,收到請求后即返回表示這次連接的Socket對象;開啟新的線程專門處理這個連接;獲得連接的輸入輸出流,并按照一定的規(guī)則進行數(shù)據(jù)交換;關(guān)閉連接(關(guān)閉連接時會自動關(guān)閉IO流).服務(wù)器端socket編程的主要代碼如下:
public class ServerTest{
public static void main(String[]args){
try{
ServerSocket server=new ServerSocket(10002);
while(true){
Socket socket=server.accept();
MyThread myThread=new MyThread(socket);
myThread.start();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
在Java中實現(xiàn)socket編程,客戶端要做的事情主要有:創(chuàng)建Socket對象,即向服務(wù)器申請連接;獲得連接的輸入輸出流,并按照一定的規(guī)則進行數(shù)據(jù)交換;最后關(guān)閉連接(關(guān)閉連接時會自動關(guān)閉IO流).客戶端socket編程的主要代碼如下:
public class ClientTest{
public static void main(String[]args)throws IOException{
Socket socket=new Socket("localhost",10001);
InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream();
byte[]buff=new byte[1024];
int len=inputStream.read(buff);
System.out.println(new String(buff,0,len));
socket.close();
}
}