Java第五週
lambda表達式
思想 “說重點”
lambda表達式的核心即在於說重點
以線程代碼爲例,他需要的是什麼?最核心的東西是什麼?
就是run方法!!!
runnable接口中重寫實現的就一個run方法!!!接口只是run方法的載體,裝黃桃的罐頭盒子!!!核心只要run方法。
我們爲什麼要寫runnable接口的實現類?就是爲了重寫run方法,並且讓線程去執行run方法。
所以,現在我們不要盒子了,直接用手抓着喫!!!
Lambda表達式格式
- service.submit(() -> System.out.println(Thread.currentThread().getName()));
() -> System.out.println(Thread.currentThread().getName())
Lambda表達式
() 參數列表
-> 做什麼事情,就是對應方法體
箭頭之後的代碼就是正常語句
(參數列表) -> {代碼語句}
Lambda表達式使用,無參數無返回值
Lambda表達式使用,有參數有返回值
Lambda表達式使用前提
- 有且只有一個缺省屬性爲public abstract方法的接口,例如 Comparator接口,Runnable接口
使用lambda表達式是有一個前後要求約束的方法的參數爲接口類型,或者說局部變量使用調用方法,可以使用lambda也OK
有且只有一個抽象方法的接口,稱之爲【函數式接口】Comparator接口,Runnable接口
反射
Java文件和.class文件的關係
- Java文件
Java文件中包含代碼的所有內容,類,接口,成員變量,成員方法…
.class字節碼文件
.java文件 通過 javac編譯工具生成對應的.class字節碼文件
使用JDK中提供的反編譯工具,可以看到.class文件中包含
Class 完整的包名.類名
Field 成員變量,成員變量的名字和成員變量的數據類型[如果是引用數據類型,也是
完整的包名.類名]
Method 成員方法,方法權限修飾符,返回值類型,方法名,形式參數列表數據類型
總結:
.class字節碼文件中,包含了Java文件的所有內容
程序加載過程和.class文件的關係
- 在Java文件運行過程中,當前程序需要哪一個類參與代碼執行,那麼就需要加載這個類的.class字節碼文件,該.class字節碼文件在程序的加載階段,存在於內存的【代碼區】
.class字節碼文件既然加載到內存的【代碼區】
.class文件中包含對應Java程序的所有內容
代碼區存在一塊空間 ==> .class ==> Java程序的所有內容
Java中的萬物皆對象
- 在Java代碼中,把在內存代碼區保存的.class字節碼內存空間,看做是一個對象。而該對象中包含了對應Java文件的所有內容。
我的理解:是否和方法名,引用數據類型,數組名之類的類似?都是空間地址。不過他們都是堆區空間地址,而這個對象是代碼區空間地址。
- 我的理解:java文件反編譯形成的.class文件,會在代碼區佔據一片空間,保存java文件所有內容,也就是構造方法,成員變量,成員方法,註解。而把這片空間看做是一個對象,這個對象是Class類型的。
反射必會方法【重點】
-
Class涉及到的方法(獲取Class類對象)
- Class Class.forName(String packageNameAndClassName);
Class類的靜態成員方法,通過完整的包名.類名 獲取對應.class文件的Class對象
同時也可以作爲.class文件加載的方式。
- Class Class.forName(String packageNameAndClassName);
Class 類名.class;
通過類名.class方法,獲取對應的Class類對象,通常用於方法的參數類型。
Class 類對象.getClass();
通過類對象獲取對應的.class的Class類對象,方法參數,或者說數據類型判斷。
-
Constructor 構造方法類涉及到的方法(通過Class類對象獲取對應類對象的構造方法)
- 這裏有四種方法
1.public Constructor[] getConstructors();
2.public Constructor[] getDeclaredConstructors();
3.public Constructor getConstructor(Class… initArgumentTypes);
4.public Constructor getDeclaredConstructor(Class… initArgumentTypes);
具體如下:
public Constructor[] getConstructors();
獲取當前Class類對象對應Java文件中,所有【public修飾構造方法的類對象數組】
public:方法權限修飾符
Constructor[]:返回值類型,數組
getConstructors():方法名,並且無參數
下邊的方法就不一一贅述。
public Constructor[] getDeclaredConstructors();
【暴力反射】
獲取當前Class類對象對應Java文件中,所有【構造方法的類對象數組】,包括私有化構造方法。回顧】
new Person();
new Person(1);
因爲這裏利用了重載的知識點,會根據實際【參數類型】,來選擇對應的構造方法。
【推理】
通過Class類對象,獲取指定構造方法,需要根據構造方法的所需的參數數據類型來完成。
public Constructor getConstructor(Class… initArgumentTypes);
根據指定的數據類型,來選擇對應的構造方法,這裏可能會拋出異常。
這裏有且只能獲取獲取類內的指定數據類型public修飾構造方法類對象
Class: 約束數據類型,當前方法所需的參數類型
例如:
這裏需要int類型 int.class
這裏需要String類型 String.class
之類需要Perosn類型 Person.class
異常:
NoSuchMethodException
… : 不定長參數
構造方法需要的參數類型是很多的,有可能無參數,有可能有參數。… 不定長參數
類約束使用,增強代碼的普適性
例如:
這裏無參數 () or (null)
參數類型int類型 (int.class)
參數類型int, String類型 (int.class, String.class)
initArgumentTypes:
參數類型 初始化參數類型複數
public Constructor getDeclaredConstructor(Class… initArgumentTypes);
【暴力反射】
根據指定的數據類型,來選擇對應的構造方法,這裏可能會拋出異常。
這裏可以獲取指定參數類型私有化構造方法和非私有化構造方法
Class: 約束數據類型,當前方法所需的參數類型
例如:
這裏需要int類型 int.class
這裏需要String類型 String.class
之類需要Perosn類型 Person.class
異常:
NoSuchMethodException
… : 不定長參數
構造方法需要的參數類型是很多的,有可能無參數,有可能有參數。… 不定長參數
類約束使用,增強代碼的普適性
例如:
這裏無參數 () or (null)
參數類型int類型 (int.class)
參數類型int, String類型 (int.class, String.class)
initArgumentTypes:
參數名 初始化參數類型複數
-
通過Class對象創建Class對象對應的類對象
- Object newInstance(Object… initArguments);
通過Constructor對象來調用,傳入當前構造方法所需創建對象的初始化參數,創建對象。
Object: Object類是Java中所有類的基類,這裏可以傳入任意類型的參數
… : 不定長參數,因爲Constructor類對象在獲取的過程中,約束的參數個數都不確定,
這裏使用不定長參數來傳入數據
- Object newInstance(Object… initArguments);
-
Method成員方法涉及到的方法
- Method[] getMethods();
獲取類內所有public修飾的成員方法,包括從父類繼承而來的public修飾方法。
- Method[] getMethods();
Method[] getDeclaredMethods();
暴力反射
獲取類內所有成員方法,但是不包括從父類繼承而來的方法。Method getMethod(String methodName, Class… parameterTypes);
根據指定的方法名和對應的參數類型,獲取對應的public修飾的成員方法
methodName:
方法名,指定獲取的是哪一個方法
parameterTypes:
Class用於約束當前使用你的參數數據類型
… 不定長參數,方法參數個數,順序,有參無參問題
例如:
cls是Class類對象
cls.getMethod(“setName”, String.class);
cls.getMethod(“getName”);
Method getDeclaredMethod(String methodName, Class… parameterTypes);
根據指定的方法名和對應的參數類型,獲取對應的成員方法,包括私有化成員方法,但是不
包括從父類繼承而來的方法
methodName:
方法名,指定獲取的是哪一個方法
parameterTypes:
Class用於約束當前使用你的參數數據類型
… 不定長參數,方法參數個數,順序,有參無參問題
例如:
cls是Class類對象
cls.getMethod(“setName”, String.class);
cls.getMethod(“getName”);
- Object invoke(Object obj, Object… arguments);
通過Method類對象調用,執行對應的方法,需要的參數
obj :
執行當前方法的執行者
arguments:
Object… 不定長參數,當前方法執行所需的實際參數,
-
Field成員變量涉及到方法
- Field[] getFields();
獲取類內所有public修飾的成員變量
Field[] getDeclaredFields();
獲取類內所有成員變量,包括私有化成員方法
- Field[] getFields();
Field getField(String fieldName);
獲取指定變量名的成員變量對象,要求是public修飾的成員變量
Field getDeclaredField(String fieldName);
獲取指定變量名的成員變量對象,包括private私有化修飾的成員變量
void set(Object obj, Object value);
設置指定調用者中對應成員變量的數據
obj : 調用者
value: 對應當前成員變量需要賦值的內容
Object get(Object obj);
獲取指定調用者中指定成員變量的數據
obj: 調用者
-
給予暴力反射私有化內容的權限操作
- setAccessible(boolean flag);
給予Constructor,Method, Field對象,私有化內容,操作權限設置
true表示可以操作
- setAccessible(boolean flag);
網絡編程
C/S和B/S
-
C/S
- 客戶端 服務器軟件結構
服務提供商給予用戶服務需要準備的內容
1. 各大平臺的客戶端
Android iOS PC Windows Linux macOS
QQ 微信 淘寶 JD 劍與遠征
2. 服務器提供服務
軟件更新:
LOL服務器版本更新,同時本地軟件也要進行更新操作。這個操作非常耗時。
熱更新
-
B/S
- 瀏覽器 服務器軟件結構
服務提供商只要提供數據服務就OK,以及前端數據展示方式
1. 瀏覽器提供商非常非常多
谷歌,火狐,歐朋,Safari,Edge
2. 服務器提供服務
軟件更新:
服務器更新數據,瀏覽器刷新就ok了
網絡通信協議
-
協議
- protocol協議
網絡通信協議是要求雙方傳遞數據的計算機必須遵守的,按照對應的網絡傳輸協議,纔可以進入數據的交互和傳遞。
目前網絡段數據傳輸比較常見的協議:
UDP TCP/IP
UDP和TCP/IP區別
-
UDP
-
- 面向無連接,數據傳遞不算特別安全
-
- 因爲面向無連接,傳輸速度快
- 因爲面向無連接,數據傳遞存在丟包問題
- UDP沒有客戶端和服務器區別,都可以作爲發送端和接收端,相互的
UDP協議使用場景
直播,網絡遊戲
實時的大部分都是UDP
-
TCP/IP
-
- 面向連接,數據傳遞較爲安全
-
- 因爲面向連接,所有傳遞速度較慢
- 面向連接,數據傳遞有保障
- TCP/IP協議是有明確的服務器和客戶端概念
TCP/IP協議使用場景
客戶端登陸,數據下載,文件傳輸
一個軟件肯定是混合協議的,不是單獨的。
網絡編程的三要素
-
協議
- 兩個在於網絡情況下的計算機數據傳遞,都需要對應的協議來完成。
-
IP地址
- Internet Protocol Address
當前計算機在網絡中的一個地址編號,類似於手機號號碼
IP地址有IPv4協議和IPv6協議
IPv4是一個32位的二進制數,通常展示效果是a.b.c.d 例如 192.168.1.1
a.b.c.d 各代表0 ~ 255的數字,目前已經消耗殆盡 42億個
IPv6
IPv6是能夠保證地球上的每一粒沙子都有一個IP地址。
128位地址長度,16字節一組
8組 0x0 ~ 0xFFFF
- Internet Protocol Address
-
端口號
- 端口號是當前應用程序在計算機中的一個編號。可以讓計算機明確知道,當前的數據是給予哪一個程序使用,或者數據從哪一個程序出現的。
端口號是一個short類型 0 ~ 65535
0~1024不能用於自定義端口號使用,特定的系統端口號
- 端口號是當前應用程序在計算機中的一個編號。可以讓計算機明確知道,當前的數據是給予哪一個程序使用,或者數據從哪一個程序出現的。
IP類
- SUN公司提供給開發使用的IP地址類
InetAddress
常用方法:
InetAddress getLocalhost();
獲取本機IP地址類對象
InetAddress getByName(String str);
根據指定的主機名獲取對應的IP地址對象
InetAddress[] getAllByName(String str);
獲取指定主機名,或者域名對應的所有IP地址類對象
UDP協議數據傳輸
-
UDP數據傳輸方式
- User Datagram Protocol
數據傳遞採用數據包方式傳遞,所有的數據要進行打包操作,並且沒有對應的客戶端服務器概念,有且只有發送端和接收端
- User Datagram Protocol
Socket 套接字
數據需要進行傳遞操作,在數據傳遞的兩臺計算機當中必須有對應的Socket。這裏採用UDP協議,那麼必須有一個UDP協議的Socket
DatagramSocket();
創建一個發送端UDP協議Socket對象
DatagramSocket(int port);
創建一個接收端UDP協議的Socket對象,這裏需要【監聽】指定端口
發送端數據包的打包方法:
DatagramPacket DatagramPacket(byte[] buf, int length, InetAddress address, int port);
buf: 需要傳遞數據的字節數組
length:是當前字節數組中數據容量字節數
address:接收端IP地址對象
port: 接收端對應的端口號
接收端數據包接收方式
這裏需要準備一個空的數據包
DatagramPacket DatagramPacket(byte[] buf, int length);
buf: 字節緩衝數組,通常是1024整數倍
length: 當前字節緩衝數組的容量
-
發送端
- 流程:
- 創建UDP服務器對應的發送端Socket
- 準備對應數據包,需要帶有指定數據
- 發送數據 send
- 關閉UDP發送端
-
接收端
- 流程:
- 打開UDP服務,並且監聽指定端口
- 創建新的空數據包
- 通過Socket接收數據
- 關閉UDP服務接收端
-
UDP數據傳遞丟失問題
- 這是udp的缺點之一,因爲面向無連接,所以可能會丟數據,類似於玩電腦遊戲丟包,瞬移,卡頓。
網絡不夠好,穩定性不行,帶寬不夠
電腦性能不好
TCP
-
TCP概述
- TCP相對於UDP比較穩定的傳輸協議,這裏存在三次握手,保證連接狀態,同時有明確的客戶端和服務端之分
TCP服務中需要服務器端先啓動,需要監聽指定端口,等待客戶端連接。
客戶端主動連接服務器,和服務器連接之後,纔可以進行數據交互,服務器不能主動連接客戶端的。
TCP操作而言,Java中提供了兩個Socket
服務端Socket
java.net.ServerSocket;
創建對應的ServerScoket開啓服務器,等待客戶端連接
客戶端Socket(這個比較重要)
java.net.Socket
創建客戶端Scoket,並且連接服務器,同時將Socket發送給服務器綁定註冊。
-
Socket 客戶端Socket
- 給客戶端提供數據傳輸的符合TCP/IP要求的Socket對象
構造方法 Constructor
Socket(String host, int port);
host是服務器IP地址,port對應服務器程序的端口號
通過指定的服務器IP地址和端口號,獲取TCP連接對象
成員方法 Method
InputStream getInputStream();
獲取Socket對象輸入字節流,可以從服務器獲取對應的數據
InputStream是一個資源,需要在程序退出是關閉
Read
OutputStream getOutputStream();
獲取Sokcet對象輸出字節流,可以發送數據到服務器
OutputStream是一個資源,需要在程序退出是關閉
Write
void close();
關閉客戶端Socket
void shutdownOutput();
禁止當前Socket發送數據
TCP/IP協議對應的Socket是給予IO流實現的。
-
ServerSocket服務端Socket
- 在服務端開啓Socket服務器
構造方法 Constructor:
ServerSocket(int port);
開啓ServerSocket服務器,並且明確當前服務端口是誰
成員方法 Method:
Socket accept();
監聽並且連接,得到一個Socket對象,同時該方法是一個阻塞方法,會處於一個始終的監聽狀態
返回的是Socket,也就是客戶端Socket對象,獲取到當前Socket對象,相對於獲取到客戶端連接,同時使用的Socket和客戶端一致。
-
TCP協議代碼演示
-
服務器代碼
- 流程:
-
-
創建ServerSocket服務器,同時監聽指定端口
-
通過accept方法獲取Socket連接,得到客戶端Socket對象
-
通過Socket對象,獲取InputStream,讀取客戶端發送數據
-
通過Socket對象,獲取OutputStream,發送數據給客戶端
-
關閉服務
-
客戶端代碼
- 流程:
-
-
創建Socket服務,同時明確連接服務器的IP地址和對應端口號
-
通過Socket對象,獲取對應的OutputStream對象,發送數據給服務器
-
通過Socket對象,獲取對應的InputStream對象,接收服務器發送數據
-
關閉服務
-
代碼總結
- 在這裏只是一個小的演示,傳遞的只有一句話,比較小,所以用一個1024的字節數組就可以接收信息了。
下邊來傳輸比較大的文件,會用到之前的IO流操作。
- 在這裏只是一個小的演示,傳遞的只有一句話,比較小,所以用一個1024的字節數組就可以接收信息了。
-
-
文件上傳操作
-
客戶端程序
- 流程:
-
-
創建對應文件的輸入字節流操作,這裏可以使用緩衝
-
啓動Socket
-
獲取Socket輸出OutputStream對象,發送數據給服務器
-
邊讀邊發
-
當文件讀取結束,發送完畢,關閉客戶端
-
服務端程序
- 流程:
-
-
開啓服務端服務,創建ServerSocket對象
-
明確保存文件的位置,創建對應文件夾的輸出緩衝字節流
-
讀取數據,寫入文件
-
關閉服務器
-
目前服務端代碼問題
- 在上邊的代碼中,我們存在一些邏輯問題
-
保存的文件名都是一致的,無法保存多個文件。
這裏可以考慮使用UUID作爲文件名
服務端沒有這麼low,代碼肯定不能執行完一個上傳功能就結束
同理,服務端代碼不可能只有一個上傳文件功能
- 服務端代碼優化
- 代碼
XML
XML概述
- Extensible Markup Language
可拓展標記語言
用途:
1. 數據存儲,小型數據庫,存在一定的CRUD操作可行性
2. 網絡端數據的傳輸
3. JavaWEB框架項目配置文件
Spring Druid …
w3c萬維網聯盟指定的規範
基本語法
-
- XML文件後綴名是.xml
- XML第一行是對於當前文件的定義聲明
- XML文件中有且只有一個根標籤
- 屬性值必須使用引號包含,這裏推薦使用雙引號
- 標籤必須正確匹配,正確開始和關閉
- XML標籤內嚴格區分大小寫
XML文件組成部分
-
- 文檔聲明:
a. 格式:
<?xml 屬性列表 ?>
<?xml version="1.0" encoding="utf-8" ?>
version: 當前XML文件版本號
encoding: 編碼方式,這裏建議XML文件的保存編碼集和對應的解析編輯一致。
standalone:是否依賴於其他文件 [瞭解]
yes 不依賴, no 依賴
- 文檔聲明:
-
指令(瞭解)
這裏可以導入一些CSS樣式
<?xml-stylesheet type="text/css" href="test.css" ?> -
標籤內容自定義
規則:
a. 自定義標籤允許使用英文字母,數字和其他標點符號(_ - .)
b. 不允許使用數組和標點符號開頭,只能用英文字母
c. 不允許在自定義標籤內使用xml標記,XML也不行
d. 名字不允許出現空格 -
屬性
可以給標籤一個屬性,有時候要求ID屬性是惟一的 -
文本(瞭解)
CDATA區,所見即所得,CDATA區內容是完整展示的
格式:
<![CDATA[ 數據 ]]>
XML文件數據約束
-
- DTD
一種簡單的約束方式
但是存在一定的約束問題
- DTD
- Schema
一種複雜XML文件約束方式
非常嚴謹
- DTD約束
- Schema約束
XML解析
-
XML解析思路
- DOM解析
Document Object Model 文件對象模型
把XML整個文件看做一個Document對象,每一個節點看做一個Element,節點中有Attribute,或者當前節點中存在Text文本內容。
DOM是將整個XML文件讀取到計算機內存中,可以進行CRUD操作。
缺點:
佔用了大量內存空間
適用的環境:
服務器對於XML文件的解析過程。
- DOM解析
SAX解析
逐行讀取,給予一定的事件操作。
讀取一行內容,釋放上一行內容,可以有效的節約內存空間
缺點:
不能對XML文件,進行增刪改
適用的環境:
手機讀取解析XML文件時採用的方式。
-
XML文件解析工具
-
- JAXP: SUN提供的一個基本的解析器,支持DOM和SAX兩種解析方式,但是操作很繁瑣,不便於程序員開發。
-
-
Dom4j: DOM For Java 一款非常優秀的解析器
Spring,SpringMVC… 框架中集成的XML解析器 -
Jsoup: 基於Java完成的對於HTML解析的工具,因爲HTML和XML文件都是標記語言。
給Jsoup一個URL,頁面地址. Java的小爬蟲,API很多很方便 -
PULL:
Android手機上集成的XML解析工具,SAX方式解析
-
Dom4j使用入門
-
- 導包
目前使用的是第三方工具,不是原生的JDK
導入第三方Jar包
- 導包
-
-
設置IDEA
-
Dom4j涉及到的方法
SAXReader();
解析XML文件使用的核心類
read() --> XML文件Document對象
Document document = new SAXReader().read(new File("./xml/User.xml"));Document對象中可以使用方法
Element getRootElement();
獲取當前XML文件的根節點對象Element對象中可以使用方法
List elements();
當前節點下的所有子節點
List elements(String name);
當前節點下所有指定名字的子節點
Element element();
獲取當前節點下的第一個子節點
Element element(String name);
獲取當前節點下指定名字的第一個子節點
Attribute getAttribute(String name);
根據屬性名獲取對應的屬性對象Attribute
Attribute節點中可以使用String getValue()來獲取對應的節點數據
String getName();
獲取當前節點的名字
String getText();
獲取當前節點對應的文本數據
-
XML文件保存
- 流程:
- 創建Document對象
- 通過Document對象來添加元素
addElment();
addAttribute();
Java線程初識
進程是什麼
- windows電腦中,打開任務管理器,可以看到電腦中執行的每一個程序,每一個程序就是一個進程。
Windows系統是一個多任務系統。
電腦可以同時執行多個程序。
線程是什麼
- 電腦管家是一個軟件,也是程序 ==> 進程
電腦可以同時開啓 病毒查殺,垃圾清理,一鍵加速…等功能
每一個功能就可以看做是線程!
一個應用程序 ==> 進程
應用程序的某一個功能 ==> 線程
應用程序中可以同時執行多個功能 ==> 多線程
線程使用的是系統資源,該系統資源你是操作系統分配給當前進程使用的。
多個線程的情況下,同時【搶佔執行】會導致資源緊缺。
線程搶佔過程就類似於進程搶佔過程。
一個Java程序,最少有幾個線程?
2個線程
main線程
JVM的GC機制,守護線程。
併發和並行
- 併發:
兩個或者兩個以上的事務在同一個時間段發生
並行:
兩個或者兩個以上的事務在同一個時刻發生
宏觀並行,微觀串行
高併發
雙十一
JD 618
12306
中午下課的餐廳
同時在一個時間段以內,很多事情都發生了,這就是高併發。
多線程
-
多線程的優缺點
- 優點
- 提升資源利用率
- 提高用戶體驗
缺點:
1. 降低了其他線程的執行概率
2. 用戶會感受到軟件的卡頓問題
3. 增加的系統,資源壓力
4. 多線程情況下的共享資源問題,線程衝突,線程安全問題
-
創建自定義線程類的兩種方式
- class Thread類
Java中的一個線程類
Thread類是Runnable接口的實現類,同時提供了很多線程的操作使用的方法。
- class Thread類
interface Runnable接口
這裏規定了what will be run?
裏面只有一個方法 run方法
- **方式一:**
自定義線程類,繼承Thread類,重寫run方法
創建自定義線程對象,直接調用start方法,開啓線程
- **方式二:**
自定義線程類,遵從Runnable接口
使用自定義遵從接口Runnable實現類對象,作爲Thread構造方法參數
藉助於Thread類對象和start方法,開啓線程
【推薦】
以上兩種方式,推薦使用方法二,遵從Runnable接口來完成自定義線程,不影響正常的繼承邏輯,並且可以使用匿名內部類來完成線程代碼塊的書寫。
Thread類需要了解的方法
- 構造方法 Constructor
Thread();
分配一個新的線程對象,無目標,無指定名字
Thread(Runnable target);
創建一個新的線程對象,並且在創建線程對象的過程中,使用Runnable接口的實現類
對象作爲執行的線程代碼塊目標
Thread(String name);
創建一個新的線程,無指定目標,但是指定當前線程的名字是什麼
Thread(Runnable target, String name);
創建一個線程的線程對象,使用Runnable接口實現類對象,作爲執行目標,並且指定name作爲線程名
成員方法:
void setName(String name);
String getName();
以上兩個是name屬性setter和getter方法
void setPriority(int Priority);
設置線程的優先級,非一定執行要求,只是增加執行的概率
優先級數值範圍 [1 - 10] 10最高 1最低 5默認
int getPriority();
獲取線程優先級
void start();
啓動線程對象
public static void sleep(int ms);
當前方法是靜態方法,通過Thread類調用,要求是當前所在線程代碼塊對應的線程,
進行休眠操作,休眠指定的毫秒數
public static Thread currentThread();
當前方法是靜態方法,通過Thread類調用,獲取當前所處代碼塊對應的線程對象。
這些方法必須牢牢記住!
線程安全問題–共享資源能使用問題
- 例如:
<<湄公河行動>>
100張票
淘票票CGV 美團 貓眼
三個銷售渠道,100張票是一個共享資源!!!
三個銷售渠道,可以認爲是三個銷售線程!!!
問題一:
100張票共享資源問題,選什麼來保存?
局部變量:
在方法內,如果run方法執行,存在,run方法當前執行完畢,銷燬。
每一個線程對象中都有run方法,無法滿足共享問題
成員變量:
每一個線程對象中,都有一個對應的成員變量,非共享資源。
靜態成員變量:
屬於類變量,所有的當前類對象,使用的靜態成員變量都是一個,而且一處修改,處處
受影響。【共享資源】
問題二:
資源衝突問題
線程之間會相互搶佔,而且搶佔頻率很快,有可能會導致一張票賣了三次,也就是資源衝突問題
————————————————
版權聲明:本文爲CSDN博主「青檸小魚碼字猴」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_42581682/article/details/104815540
-
鎖
- 所以爲了解決這個問題,我們用鎖來解決
以下鎖的是方法,無論誰調用,怎麼調用都會鎖住
- 同步代碼塊
- 格式:
synchronized (/* 鎖對象 */) {
}
特徵:
- synchronized 小括號裏面的對象是鎖對象,並且要求如果是多線程的情況下,鎖對象必須是同一個對象。也就是runnable接口實現類對象。
- synchronized 大括號中的代碼塊就是需要進行同步的代碼,或者說是加鎖的代碼,大括號裏面的內容,有且只允許一個線程進入。
- 同步代碼塊越短越好,在保證安全的情況下,提高性能
問題:
-
目前鎖對象感覺很隨意,存在一定的隱患
-
代碼層級關係很複雜,看着有點麻煩
這個方法比較隨意,一把鎖會鎖了多個線程,有隱患-
同步方法
- 定義在線程類內
-
synchronized 作爲關鍵字來修飾方法,修飾的方法就是對應的同步方法
有且只允許一個線程進入,到底是誰來完成的加鎖操作?
靜態成員方法
鎖對象,是當前類對應的字節碼文件.class
就是類名.class
非靜態成員方法
鎖對象就是當前類對象 this
選擇同步方法是否使用static修飾問題
如果非static修飾,要保證執行的線程對象有且只有一個,因爲鎖對象就是當前線程對象
如果是static修飾,鎖對象具有唯一性,多個線程使用的鎖是同一個鎖。類似於同步代碼塊。
- Lock鎖
- Java提供了一個對於線程安全問題,加鎖操作相對於同步代碼塊和同步方法更加廣泛的一種操作方式。
對象化操作。
創建Lock構造方法
Lock lock = new ReentrantLock();
多態思想,不理解就先記着這麼用。
方法化操作。
開鎖:
unlock();
加鎖:
lock();
- 三種加鎖方式的總結
- 一鎖一線程,一鎖多線程問題。
使用對應的鎖操作對應的線程,考慮靜態和非靜態問題。
同步方法和Lock鎖使用。
靜態是一鎖多目標,非靜態是一鎖一目標
涉及到同步問題時,要考慮好鎖對象的選擇問題
同步代碼塊,同步方法,Lock對象
守護線程
- 守護線程,也稱之爲後臺線程,如果當前主線程GG思密達,守護線程也就GG思密達。
守護線程一般用於:
- 自動下載
- 操作日誌
- 操作監控
方法是通過線程對象
setDeamon(boolean flag);
true爲守護線程
false缺省屬性,正常線程
線程的狀態簡說
-
NEW(新建) 線程剛剛被創建,沒有啓動,沒有調用start方法
RUNNABLE(可運行) 線程已經可以在JVM中運行,但是是否運行不確定,看當前線程是否擁有CPU執行權
BLOCKED(鎖阻塞) 當前線程進入一個同步代碼需要獲取對應的鎖對象,但是發現當前鎖對象被其他線程持有,當前線程會進入一個BLOCKED。如果佔用鎖對象的線程打開鎖對象,當前線程持有對應鎖對象,進入Runnable狀態
WAITING(無限等待) 通過一個wait方法線程進入一個無限等待狀態,這裏需要另外一個線程進行喚醒操作。進入無限等待狀態的線程是無法自己回到Runnable狀態,需要其他線程通過notify或者notifyAll方法進行喚醒操作
TIMED_WAITING(計時等待) 當前線程的確是等待狀態,但是會在一定時間之後自動回到Runnable狀態,例如 Thread.sleep() 或者是Object類內的wait(int ms);
TERMINATED(被終止) 因爲Run方法運行結束正常退出線程,或者說在運行的過程中因爲出現異常導致當前線程GG思密達 -
TIMED_WAITING(計時等待)
- Thread.sleep(int ms);
在對應線程代碼塊中,當前線程休眠指定的時間。
- Thread.sleep(int ms);
Object類內 wait(int ms);
讓當前線程進入一個計時等待狀態
- 規定的時間及時完畢,線程回到可運行狀態
- 在等待時間內,通過其他線程被notify或者notifyAll喚醒
Sleep方法
- 調用之後休眠指定時間
- sleep方法必須執行在run方法內,纔可以休眠線程
- sleep不會打開當前線程佔用的鎖對象。
————————————————
版權聲明:本文爲CSDN博主「青檸小魚碼字猴」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_42581682/article/details/104818483
-
BLOCKED(鎖阻塞)
- 線程中有鎖存在,線程需要進入帶有鎖操作的同步代碼,如果鎖對象被別人持有,只能在鎖外等待
鎖阻塞狀態的線程是否能夠搶到鎖對象有很多因素
- 優先級問題,非決定因素
- CPU執行概率問題。
後期高併發一定會存在多線程操作鎖對象問題,秒殺,搶購…
隊列方式來處理
-
線程狀態 WAITING(無限等待)
- 當某一個線程被執行wait()方法,需要等待另外的一個線程進行喚醒操作。
以下三個方法都是Object類內的方法:
public void wait();
在哪一個線程中執行,就會讓當前線程進入一個無限等待狀態。
- 所在線程進入無限等待狀態
- 開啓【鎖對象】
public void notify();
喚醒和當前鎖對象有關的無限等待線程中的一個,隨機選擇。
- 喚醒一個無限等待狀態線程
- 開啓【鎖對象】
public void notifyAll();
喚醒所有和當前鎖對象有關的無限等待線程
- 喚醒所有線程
- 開啓【鎖對象】
- 線程進入鎖對象搶佔過程,就有可能進入一個鎖阻塞狀態。
————————————————
版權聲明:本文爲CSDN博主「青檸小魚碼字猴」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_42581682/article/details/104818483
線程通信
-
共享資源處理問題
- 現在存在兩個完全無關的線程:生產者和消費者,但是商品會作爲他們兩者之間的共享資源。
生產者和消費者中都有一個成員變量 商品類型
- 現在存在兩個完全無關的線程:生產者和消費者,但是商品會作爲他們兩者之間的共享資源。
【解決方案】
創建生產者或者消費者線程對象時,使用同一個商品類對象,作爲構造方法參數進行初始化操作
- 代碼
- 子主題 1
線程池
我們在之前的線程學習中,都是之間創建新的線程,顯性線程,用的時候開啓,用完銷燬,效率低且不安全
而且我們看到在阿里巴巴代碼規範規約中也是不建議顯式創建線程,建議使用線程池。
- 不管是繼承Thread還是遵從Runnable接口,都需要重寫Run方法,而且每一個線程對象有且只能執行一次,之後就會被銷燬。
利用Runnable接口來提供執行目標,而且藉助於Thread執行線程。
- 用生活中的例子來理解:
一個餐廳
服務人員
餐廳會按照餐桌比例安排服務員人數。
每一個服務員我們都可以看做是一個線程對象
需要告知服務器做什麼事情就可以了,相對於告知線程對象執行目標是什麼
當你來餐廳之前,服務員在這裏,你走之後,服務員依然在這類。
線程池 ==> 可以容納多個線程的容器
程序可以從線程池獲取線程來完成目標代碼
同時也可以將線程歸還給線程池。
省去了創建線程和銷燬線程這樣非常繁瑣的操作。節省時間。
- 線程池使用
public static ExecutorService newFixedThreadPool(int nThreads);
得到一個線程池對象,初始化參數是要求的當前線程池中的線程數
在這行代碼中, newFixedThreadPool是ExecutorService類中的方法
返回值是ExecutorService,參數是int類型的線程數量。
創建代碼示例爲:
ExecutorService service = Executors.newFixedThreadPool(5);
public Future submit(Runnable target);
從線程池中獲取一個線程對象,並且執行給定的Runnable接口實現類對象作爲執行目標
XMind: ZEN - Trial Version