Day20-網絡
1. 線程(續)
juc 中的大部分類是通過無鎖併發實現的(沒有用synchronized)
CAS 機制 compare And swap 比較並交換
synchronized 可以稱之爲悲觀鎖
cas 體現的是樂觀鎖
首先不會給共享資源加鎖,而是做一個嘗試
先拿到舊值,查看舊值是否跟共享區域的值相等
如果不等,那麼說明別的線程改動了共享區域的值,我的修改失敗
如果相等,那麼就讓我的修改成功
如果修改失敗,沒關係,重新嘗試
int var5;
// 修改失敗,沒關係,重新嘗試 自旋
do {
// 獲取共享區域的最新值
var5 = this.getIntVolatile(var1, var2); // 10
// 比較並交換 最新值 最新值+1
} while(! this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
1. 重入鎖 ReentrantLock
.lock() 加鎖
.unlock() 解鎖
例子:
static int i = 0;
public static void main(String[] args) throws InterruptedException {
ReentrantLock rl = new ReentrantLock();
Thread t1 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
try {
rl.lock(); // 加鎖
i++;
} finally {
rl.unlock(); // 保證解鎖一定被執行
}
}
});
Thread t2 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
try {
rl.lock(); // 加鎖
i--;
} finally {
rl.unlock(); // 保證解鎖一定被執行
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
synchronized 性能上比較 ReentrantLock 在高併發下低,ReentrantLock的內存佔用會高一些
2. CountDownLatch
countdown 倒計時
當希望多個線程執行完畢後,再接着做下一步操作時,
例子:
public static void main(String[] args) throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(3);// 構造方法需要指定倒計時的數字
new Thread(()->{
System.out.println("線程1開始運行"+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程1準備完成"+new Date());
cdl.countDown();
}).start();
new Thread(()->{
System.out.println("線程2開始運行"+new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程2準備完成"+new Date());
cdl.countDown();
}).start();
new Thread(()->{
System.out.println("線程3開始運行"+new Date());
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程3準備完成"+new Date());
cdl.countDown();
}).start();
// 主線程等待,直到倒計時爲0
System.out.println("主線程等待");
cdl.await();
System.out.println("ready go....");
}
一個應用例子:模擬10個玩家加載進度
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
String[] all = new String[10];
for (int j = 0; j < 10; j++) {
int x = j;
new Thread(()->{
Random r = new Random();
for (int i = 0; i <= 100; i++) {
try {
Thread.sleep(r.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (all){
all[x]=(i+"%");
System.out.print("\r"+Arrays.toString(all));
}
}
latch.countDown();
}).start();
}
latch.await();
System.out.println("\nend...");
}
3. 循環柵欄
// CyclicBarrier 可循環的 屏障(柵欄)
// 當滿足CyclicBarrier設置的線程個數時,繼續執行,沒有滿足則等待
CyclicBarrier cb = new CyclicBarrier(2); // 個數爲2時纔會繼續執行
new Thread(()->{
System.out.println("線程1開始.."+new Date());
try {
cb.await(); // 當個數不足時,等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("線程1繼續向下運行..."+new Date());
}).start();
new Thread(()->{
System.out.println("線程2開始.."+new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
cb.await(); // 2 秒後,線程個數夠2,繼續運行
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("線程2繼續向下運行..."+new Date());
}).start();
與倒計時鎖的區別:倒計時鎖只能使用一次,倒計時結束這個對象就沒用了。
而循環柵欄可以重複利用。
4. 信號量
Semaphore s = new Semaphore(3); // 限制了能同時運行的線程上限
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
s.acquire(); // 獲得此信號量
System.out.println("我是線程" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release(); // 釋放信號量
}
}).start();
}
2. 網絡模型與協議
OSI 七層模式 : 應用層,表示層,會話層,傳輸層,網絡層,鏈路層,物理層
五層模型: 應用層, 傳輸層,網絡層,鏈路層,物理層
四層模型 : 應用層, 傳輸層,網絡層,鏈路層
應用層:http(超文本傳輸協議) ftp(文件傳輸協議) stmp (郵件發送協議) pop3(郵件接收協議), ssh ( 安全shell,用於遠程登錄)
傳輸層: tcp(安全可靠的協議) udp(不可靠)
網絡層:ip
windows下可以使用 ipconfig
來查看ip地址
linux 下可以使用 ifconfig
來查看ip地址
ip 地址的作用是用來定位到網絡上的另一臺計算機
port 端口 mysql 3306
oracle 1521
sqlserver 1433
redis 6379
tomcat 8080
apache(http的服務) 80
ftp 21
ssh 22
...
端口號的作用是用來標記,要訪問對方的哪個程序
傳輸層協議:
tcp協議:
TCP 協議的特點是: TCP 協議是一個有連接、可靠的協議。所謂有連接,指的是在進行 TCP通信之前,兩個需要通信的主機之間要首先建立一條數據通道,就好像打電話進行交流之前,首先要讓電話接通一樣。所謂可靠,指的是 TCP 協議能夠保證: 1、發送端發送的數據不會丟失; 2、接收端接受的數據包的順序,會按照發送端發送的包的順序接受。也就是說, TCP協議能夠保證數據能夠完整無誤的傳輸。
udp協議:
與 TCP 協議相比, UDP 是一個無連接,不可靠的協議。 即:數據的發送方只負責將數據發送出去,數據的接受方只負責接受數據。發送方和接收方不會相互確認數據的傳輸是否成功。
相對於 TCP 而言, UDP 有一個優點:效率較高。因此,當我們在對數據傳輸的正確率
不太關心,但是對傳輸效率要求較高的情況下,可以採用 UDP 協議。典型的使用 UDP 協議的是網絡語音以及視頻聊天應用。
3. java中的網絡編程
Socket API 對tcp、udp協議做了封裝,能夠連接到對方主機,收發數據
tcp的例子
建立連接
服務器端:
public static void main(String[] args) throws IOException {
// 可以與客戶端的socket建立連接 端口號一般使用4位以上的數字
ServerSocket ss = new ServerSocket(5555);
System.out.println("ready...等待客戶端連接");
// 端點
Socket socket = ss.accept();// 等待客戶端連接我服務器方法,直到有客戶端連接
}
客戶端:
public static void main(String[] args) throws IOException {
// 本機ip地址爲 127.0.0.1
// 它的別名 localhost
// 端點
Socket socket =
new Socket("127.0.0.1", 5555);
}