網絡

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. 網絡模型與協議

enter description here
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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章