Day13 多線程
3).多線程的好處:
提高程序的運行效率,提高用戶的體驗度。 線程不會因為等待某個資源而進入等待狀態
創建新的線程:
* 定義類繼承Thread
* 重寫方法run
* 創建Thread子類的對象
* 調用子類對象的方法 start()
*
* 為什么繼承Thread
* Thread類是線程對象類
* 繼承了Thread,子類也是線程對象
*
* 為什么重寫run
* Sun工程師,不清楚其他人員用線程做什么
* 全部寫在run中
*
* 為什么調用start
* 線程: JVM利用Windows中的功能實現
* start() 調用 本地方法,開啟的線程
2.Thread類的方法,可以獲取到線程名字
* String getName()
*
* Thread類方法 setName(String name)
* 設置線程名字
*
* 獲取主線程名
* Thread類,定義靜態方法
* static Thread currentThread() 返回當前線程
* 什么是當前線程: 現在運行的線程
第二種方式實現Runnable接口避免了單繼承的局限性,所以較為常用。實現Runnable接口的方式,更加的符合面向對象,線程分為兩部分,一部分線程對象,一部分線程任務。
第一種方式繼承Thread類,線程對象和線程任務耦合在一起。一旦創建Thread類的子類對象,既是線程對象,有又有線程任務。
實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。
3.創建線程程序第二個方式,接口方式
* java.lang.Runnable
* 定義類實現接口Runnable
* 重寫抽象方法run
* 創建Thread類對象,Thread(Runnable r)
* 調用Thread對象start()方法
4. 售票代碼,單獨定義方法
* pay方法,所有的代碼,全是線程操作的共享數據
* 同步整個方法,方法的定義修飾符,加同步
*
* 同步方法有鎖嗎,同步方法的鎖就是this對象
*
* 靜態同步方法有鎖嗎
* 靜態方法的對象鎖可不是this
* 鎖是 本類.class對象
* Ticket.class
當一個線程在執行操作共享數據的多條代碼過程中,其他線程參與了運算。就會導致線程安全問題的產生。
6.其實,多線程程序並不能提高程序的運行速度,但能夠提高程序運行效率,讓CPU的使用率更高。
7.思考:線程對象調用 run方法和調用start方法區別?
線程對象調用run方法不開啟線程,僅是對象調用方法。線程對象調用start開啟線程,並讓jvm調用run方法在開啟的線程中執行。
2:請描述在什么樣的情況下會出現線程安全的問題。
線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態
變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程
同時執行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。
6.java中提供了線程同步機制,它能夠解決上述的線程安全問題,請分別寫出他們的格式
3.1同步代碼塊: 在代碼塊聲明上 加上synchronized
synchronized (鎖對象) {
可能會產生線程安全問題的代碼
}
3.2同步方法:在方法聲明上加上synchronized
public synchronized void method(){
可能會產生線程安全問題的代碼
}
3.請描述線程的幾個狀態。
線程包含以下幾個狀態:
1. 新建狀態:
調用構造方法創建線程對象后,線程就會處於新建狀態。
2.就緒狀態:
一個新創建的線程並不自動開始運行,要執行線程,必須調用線程的start()
方法。在調用start()方法后,運行run()方法之前,線程就會處於就緒狀態。
3.運行狀態:
當線程獲得CPU的資源后,才會進入運行狀態,調用run方法。
4.阻塞狀態:
當正在運行的線程還沒有運行結束,暫時讓出CPU資源,讓其它處於就緒
狀態的線程獲得CPU資源。
5.死亡狀態:
當線程的run方法運行結束后,線程就結束了,此時線程就會死亡狀態。
- 7.多線程並發和多線程並行是什么呢?
答:兩個或者多個任務發送請求時,CPU只能執行一個,就會安排這些任務交替執行,由於CPU做着高速的切換,間隔的時間比較短,我們看起來像同時執行的,這就是多線程並發。 並行是兩個或多個任務同時執行,前提是多核CPU。 |
- 8.同步代碼塊和同步方法的鎖是誰?
答;同步代碼塊的鎖可以是任意類型的對象;非靜態同步方法的鎖是this;靜態方法的鎖是該類的字節碼文件。 |
- 3.sleep和wait的區別?
答:(1)sleep是讓線程睡眠,必須給相應的睡眠時間,不需要喚醒,時間到了會自動醒來,休眠時不放棄Cpu的執行權。(悲觀鎖機制) (2)wait的是讓線程等待,可以傳參也可以不傳參,傳參是在指定的時間后等待,需要被喚醒。等待的時候放棄cpu的執行權。(樂觀鎖機制) |
- 什么情況下需要同步?
答:當多線程並發, 有多段代碼同時執行時, 我們希望某一段代碼執行的過程中CPU不要切換到其他線程工作. 這時就需要同步. 如果兩段代碼是同步的, 那么同一時間只能執行一段, 在一段代碼沒執行結束之前, 不會執行另外一段代碼. |
- 開啟線程:開一個新的方法棧區
- 運行是方法run(
- 只要開啟一個線程:
- 出現一個新的方法棧,運行
當調用一個線程對象start()方法后,此線程對象中的run()方法會被立即執行.(錯誤的)
Start()方法之后進入到”就緒狀態”,等待操作系統分配cpu時間(正確的)
當一個線程對象的sleep()時間到了,會立即恢復運行(錯誤的)
當sleep()醒來后,會進入到”就緒狀態”,等待操作系統分配cpu時間(正確的)
Day14 網絡編程
InetAdderss類,該類用於封裝一個IP地址,並提供了一系列與IP地址相關的方法,下表中列出了InetAddress類的一些常用方法。
TCP協議分哪幾個層
鏈路層:鏈路層是用於定義物理傳輸通道,通常是對某些網絡連接設備的驅動協議,例如針對光纖、網線提供的驅動。
網絡層:網絡層是整個TCP/IP協議的核心,它主要用於將傳輸的數據進行分組,將分組數據發送到目標計算機或者網絡。
運輸層:主要使網絡程序進行通信,在進行網絡通信時,可以采用TCP協議,也可以采用UDP協議。
應用層:主要負責應用程序的協議,例如HTTP協議、FTP協議等。
C/S 軟件和服務器容易出現更新問題 , 一個更新全部需要更新,
B/S 網頁和服務器 維護方便,客戶端一個瀏覽器可以打開多個功能 弊端 所有瀏覽器內容存到瀏覽器
ip地址 它是由4個字節大小的二進制數來表示
HTTP、FTP、UDP、TCP
我們涉及到的:
1.UDP協議:
1.數據要打包發送;
2.數據大小有限制:64K
3.不論是否有接收端,都可以發送。也稱為:無狀態的,不安全的協議。
應用:視頻廣播,共享屏幕
2.TCP協議:
1.數據不需要打包,使用"流"的方式發送和接收;
2.數據大小無限制;
3.發送時,必須要有接收端;
1.InetAddress類表示一個IP地址;
2.InetAddress類沒有構造方法,通過靜態方法獲取InetAddress對象;
3.常用方法:
靜態方法:public static InetAddress getByName(String 計算機名/IP地址) :
普通方法:public String getHostAddress():獲取本機IP
2.區別在於,UDP中只有發送端和接收端,不區分客戶端與服務器端,計算機之間可以任意地發送數據。
而TCP通信是嚴格區分客戶端與服務器端的,在通信時,必須先由客戶端去連接服務器端才能實現通信,服務器端不可以主動連接客戶端,並且服務器端程序需要事先啟動,等待客戶端的連接。
在JDK中提供了兩個類用於實現TCP程序,一個是ServerSocket類,用於表示服務器端,一個是Socket類,用於表示客戶端。
通信時,首先創建代表服務器端的ServerSocket對象,該對象相當於開啟一個服務,並等待客戶端的連接,然后創建代表客戶端的Socket對象向服務器端發出連接請求,服務器端響應請求,兩者建立連接開始通信。
public class TCPServer {
public static void main(String[] args) throws IOException {
//1,創建服務器,等待客戶端連接
ServerSocket serverSocket = new ServerSocket(8888);
Socket clientSocket = serverSocket.accept();
//顯示哪個客戶端Socket連接上了服務器
InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址對象
String ip = ipObject.getHostAddress(); //得到IP地址字符串
System.out.println("小樣,抓到你了,連接我!!" + "IP:" + ip);
//7,獲取Socket的輸入流
InputStream in = clientSocket.getInputStream();
//8,創建目的地的字節輸出流 D:\\upload\\192.168.74.58(1).jpg
BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream("D:\\upload\\192.168.74.58(1).jpg"));
//9,把Socket輸入流中的數據,寫入目的地的字節輸出流中
byte[] buffer = new byte[1024];
int len = -1;
while((len = in.read(buffer)) != -1){
//寫入目的地的字節輸出流中
fileOut.write(buffer, 0, len);
}
//-----------------反饋信息---------------------
//10,獲取Socket的輸出流, 作用:寫反饋信息給客戶端
OutputStream out = clientSocket.getOutputStream();
//11,寫反饋信息給客戶端
out.write("圖片上傳成功".getBytes());
out.close();
fileOut.close();
in.close();
clientSocket.close();
//serverSocket.close();
}
}
public class TCPClient {
public static void main(String[] args) throws IOException {
//2,創建客戶端Socket,連接服務器
Socket socket = new Socket("192.168.74.58", 8888);
//3,獲取Socket流中的輸出流,功能:用來把數據寫到服務器
OutputStream out = socket.getOutputStream();
//4,創建字節輸入流,功能:用來讀取數據源(圖片)的字節
BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream("D:\\NoDir\\test.jpg"));
//5,把圖片數據寫到Socket的輸出流中(把數據傳給服務器)
byte[] buffer = new byte[1024];
int len = -1;
while ((len = fileIn.read(buffer)) != -1){
//把數據寫到Socket的輸出流中
out.write(buffer, 0, len);
}
//6,客戶端發送數據完畢,結束Socket輸出流的寫入操作,告知服務器端 (socket流不能判斷讀到-1 結束讀操作 )
socket.shutdownOutput();
//-----------------反饋信息---------------------
//12,獲取Socket的輸入流 作用: 讀反饋信息
InputStream in = socket.getInputStream();
//13,讀反饋信息
byte[] info = new byte[1024];
//把反饋信息存儲到info數組中,並記錄字節個數
int length = in.read(info);
//顯示反饋結果
System.out.println( new String(info, 0, length) );
//關閉流
in.close();
fileIn.close();
out.close();
socket.close();
}
1.1 文件上傳案例多線程版本
實現服務器端可以同時接收多個客戶端上傳的文件。
l 我們要修改服務器端代碼
/*
* 文件上傳多線程版本, 服務器端
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1,創建服務器,等待客戶端連接
ServerSocket serverSocket = new ServerSocket(6666);
//實現多個客戶端連接服務器的操作
while(true){
final Socket clientSocket = serverSocket.accept();
//啟動線程,完成與當前客戶端的數據交互過程
new Thread(){
public void run() {
try{
//顯示哪個客戶端Socket連接上了服務器
InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址對象
String ip = ipObject.getHostAddress(); //得到IP地址字符串
System.out.println("小樣,抓到你了,連接我!!" + "IP:" + ip);
//7,獲取Socket的輸入流
InputStream in = clientSocket.getInputStream();
//8,創建目的地的字節輸出流 D:\\upload\\192.168.74.58(1).jpg
BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream("D:\\upload\\"+ip+"("+System.currentTimeMillis()+").jpg"));
//9,把Socket輸入流中的數據,寫入目的地的字節輸出流中
byte[] buffer = new byte[1024];
int len = -1;
while((len = in.read(buffer)) != -1){
//寫入目的地的字節輸出流中
fileOut.write(buffer, 0, len);
}
//-----------------反饋信息---------------------
//10,獲取Socket的輸出流, 作用:寫反饋信息給客戶端
OutputStream out = clientSocket.getOutputStream();
//11,寫反饋信息給客戶端
out.write("圖片上傳成功".getBytes());
客戶端發送數據完畢,結束Socket輸出流的寫入操作,告知服務器端
out.close();
fileOut.close();
in.close();
clientSocket.close();
} catch(IOException e){
e.printStackTrace();
}
};
}.start();
}
//serverSocket.close();