標籤:
1. 線程概述
多任務處理有兩種類型:基於進程、基於線程(進程是指一種“自包容”的運行程式,有自己的地址空間; 線程是進程內部單一的一個順序控制流程)
基於進程的特點是允許電腦同時運行兩個或更多的程式、基於線程的多任務處理環境中,線程是最小的處理單位
2. 建立和啟動線程
A.建立線程的方法:
a. 編寫一個繼承Thread 類的類,然後重寫Thread類的run()方法
public class thread extends Thread {
public void run() {
}
public static void main(String[] args) {
thread d = new thread();
d.start();
}
}
b. 編寫一個類實現Runnable介面,然後將該類的執行個體與Thread對象聯絡在一起
public class threadrun implements Runnable { public void run(){
}
public static void main(String[] args) {
threadrun td = new threadrun();
Thread t = new Thread(td);
t.start();
}
}
3. 線程的優先順序
A. Java 中的線程優先順序是在 Thread 類中定義的常量:
NORM_PRIORITY : 值為 5
MAX_PRIORITY : 值為 10
MIN_PRIORITY : 值為 1
B . 有關優先順序的方法有兩個:
a. final void setPriority(int newp) : 修改線程的當前優先順序
b. final int getPriority() : 返回線程的優先順序
4. 使線程暫停執行的條件
A. 使用 sleep( ) 方法使線程睡眠
B. 通過調用 wait( ) 方法,使線程等待
C. 通過調用 yield( ) 方法,線程已顯式出讓CPU控制權
D. 線程由於等待一個I/O事件被阻塞
5. 線程同步:使用同步關鍵字synchronized
A.解決線程同步的方法:
a. 同步塊
文法:synchronized (取得鎖的對象){
//要鎖定的代碼
}
b.同步方法:在方法名關鍵字修飾前面使用synchronized
6. 死結: 當兩個線程循環相依性於一對同步對象時將發生死結
7. 線程之間的相互連訊
方法:
A.wait():告知被調用的線程退出監視器並進入等待狀態,直到其他線程進入相同的監視器並調用 notify( ) 方法B.Notify():通知同一對象上第一個調用 wait( )線程C.notifyAll():通知調用 wait() 的所有線程,具有最高優先順序的線程將先運行
8.OSI模型:國際標準組織ISO提供了一OSI參考模型,此模型將網路分為七層
9.TCP/IP協議A.網路主機上程式的定址定位
a . 網路編程的實質就是編寫程式直接或間接地通過網路通訊協定與其它電腦上的某個程式進行通訊
b. 如何找到網路上的主機上的要進行通訊的程式 ?
c. 提供了IP地址和連接埠號碼,就可以找到網路上指定主機上要進行通訊的指定程式
B.連接埠:用於實現程式間的通訊C.如何傳輸資料到網路上主機的程式中?
a. 資料轉送由TCP/IP分層模型中的傳輸層負責,該層包含TCP和UDP兩種協議
b. TCP協議:較可靠的雙向流協議、發送任意數量的資料、提供訊息確認、錯誤偵測和錯誤恢複等服務
c. UDP協議:比較不可靠
10 . 用戶端和伺服器
A. 伺服器和用戶端共同承擔計算
a. 客戶:向另一台電腦請求服務的電腦
b. 伺服器:處理用戶端請求的電腦
11.通訊端(Socket):是 Internet 通訊的端點,用戶端和伺服器通過通訊端建立串連和進行通訊
12.Java對網路編程的支援
JDK預定義的網路編程相關類均存放在java.net包中。
常用的類:InetAddress 、Socket、ServerSocket 和 SocketImpl、DatagramPacket 和 DatagramSocket、 URL、URLConnection 和 URLEncoder
A.InetAddress 類:是用來封裝電腦的IP地址和DNS
該類沒有構造方法,所以不能直接利用new 關鍵字建立對象。我們可以使用以下常用方法來擷取該類對象:
getLocalHost():返回表示本地主機InetAddress對象
getByName(String hostName):據主機名稱返回對象
getAddress() :返回IP地址
getHostName():取得IP地址代表的主機名稱
13.TCP通訊端編程
兩個java程式通過一個雙向的網路通訊串連實現資料交換,這個雙向鏈路的一端稱之為Socket( Socket通常用來實現客戶服務器之間的串連)
java.net包中的兩個類Socket、ServerSocket分別用來實現用戶端和服務端
A. 服務端( ServerSocket )編寫步驟:
1).利用ServerSocket建立對服務端某個連接埠的監聽
2).利用accept方法建立服務端Socket
3).利用已建立的socket建立輸入輸出資料流
4).關閉輸入輸出資料流,關閉socket,關閉server
B. 客服端( Socket )編寫步驟:
1).建立用戶端Socket向伺服器發起串連請求
2).利用已建立的socket建立輸入輸出資料流
3).關閉輸入輸出資料流,關閉socket,關閉server
14.UDP通訊端編程
A. 使用者報文協議(UDP)是用於將位元據從一台電腦發送到另一台計算的非連線協定
B. 資料報包的寄件者和接收者都使用java.net.DatagramSocket類分別發送和接收包
C.DatagramSocket類的send()和receive()方法都帶有一個DatagramPacket參數。DatagramPacket類代表一個資料報包,與DatagramSocket類似,包的寄件者和接受者都要使用它
D. 接收資料報包需要執行如下步驟:
1). 建立一個足夠大的位元組數組,用於儲存要接收的包的資料
2). 使用該位元組數組執行個體化一個DatagramPacket對象
3). DatagramSocket被執行個體化,它被指定該通訊端要綁定到的本地主機上的一個連接埠
4). 調用DatagramSocket類的receive()方法,將DatagramPacket對象傳入該方法。這將導致執行線程阻塞,直到接收到一個資料報包或者發生了逾時
E. 發送資料報包需要執行如下步驟:
1). 建立一個足夠大的位元組數組,用於儲存要發送的包資料,用該資料填充數組
2). 建立一個新的DatagramPacket 對象,用於儲存上面的位元組數組,以及伺服器名和接收者的連接埠號碼
3). DatagramSocket被執行個體化,它被指定通訊端要綁定到本地主機的哪個連接埠
4). DatagramSocket類的send()方法被調用,傳入DatagramPacket對象
例題:簡易區域網路聊天系統–區域網路QQ:
伺服器端
實現簡易的啟動停止伺服器端操作,能記錄基本日誌:客戶串連、訊息傳送,能查看串連客戶的暱稱。
啟動伺服器後開始監聽用戶端串連,建立一個新線程實現該監聽操作。
// 啟動新線程監聽用戶端
new Thread(new Runnable() {
public void run() {
writeLog("開始監聽用戶端:");
listen();
}
}).start();
為每一個用戶端串連開啟一個新線程處理通訊,包括處理輸入資料流與輸出資料流。
// 每監聽到一個用戶端串連,啟動一個新線程處理該串連
new Participant(this, socket, usersList).start();
擷取使用者不同的行為實現不同的操作 String flag = in.readUTF();
// 擷取使用者操作行為
if ("validateUser".equals(flag)) { // 驗證使用者是否存在
// ……………………
} else if ("newUser".equals(flag)) { // 新使用者登入
// ……………………
} else if ("message".equals(flag)) { // 使用者間發送訊息
// ……………………
} else if ("userList".equals(flag)) { // 擷取使用者列表
// ……………………
}
在伺服器端,採用Map儲存每個用戶端對應的通訊端:
private Map<String, Socket> usersList = new HashMap<String, Socket>();
key為用戶端串連時輸入的使用者暱稱,value為對應的通訊端對象。
各用戶端使用者間發送訊息時,通過伺服器轉寄送該資訊:
String sender = in.readUTF(); // 寄件者
String receiver = in.readUTF(); // 接收者
String message = in.readUTF(); // 訊息
// 添加日誌
server.writeLog(sender + " 向 " + receiver + "發送訊息:" + message);
// 伺服器向目標轉寄訊息,先獲得要轉寄後的目標通訊端對象
Socket socket = server.getUsersList().get(receiver);
System.out.println(out);
if (socket == null) { // 伺服器已刪除該用戶端串連資訊
out.writeUTF("message");
out.writeUTF("系統伺服器");
out.writeUTF(receiver + "已退出系統,無法再發送訊息");
out.flush();
} else {
DataOutputStream thatOut = new DataOutputStream(socket.getOutputStream()); // 建立輸出資料流對象
if (thatOut != null) {
thatOut.writeUTF("message");
thatOut.writeUTF(sender);
thatOut.writeUTF(message);
thatOut.flush(); // 寫入用戶端輸出資料流中
}
}
用戶端
先通過登入表單實現伺服器端串連,然後輸入登入使用者暱稱,如果伺服器端還未儲存該暱稱,則登入成功。登入成功後,可以直接先讀取到伺服器端已有登入使用者的暱稱,顯示到列表中。雙擊好友名單中的任一項開啟聊天對話方塊,然後雙方可以開始聊天。
建立到伺服器的通訊端對象:
// 建立通訊端對象
socket = new Socket(serverIp.getText().trim(), Integer.parseInt(port.getText().trim()));
已連線的服務器與連接埠從表單文字框中擷取到。
登入成功後,將暱稱發送到伺服器儲存:
out.writeUTF("newUser");
out.writeUTF(nickname); out.flush();
重新整理好友名單:
// 讀取流中資訊
String flag = in.readUTF();
if ("userList".equals(flag)) { // 好友名單
String[] userList = in.readUTF().split("::::"); // 擷取好友名單,使用標記分隔
// 建立好友名單模型
DefaultListModel list = new DefaultListModel();
for (int i = 0; i < userList.length; i++) {
list.addElement(userList[i]);
}
friends.setModel(list); // 設定模型
friends.validate(); // 重繪
}
發送訊息給某個好友:
out.writeUTF("message");
out.writeUTF(sender);
out.writeUTF(recipient);
out.writeUTF(textArea.getText());
out.flush();
開啟新線程讀取伺服器轉寄的好友資訊:
String flag = in.readUTF();
if ("message".equals(flag)) {
String sender = in.readUTF();
String msg = in.readUTF();
String message = sender + " 對你說:" + msg + "\n";
jTextArea.append(message);
}
多線程編程和Java網路編程