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);
}
新聞熱點
疑難解答