1. 程式人生 > >第一章 並發編程的挑戰

第一章 並發編程的挑戰

必須 currency 更新 err 如果 ide 數據庫操作 time lee

挑戰一:上下文切換

多線程一定比單線程快麽?

public class ConcurrencyTest {

    private static final long count = 10001;

    public static void main(String[] args) throws InterruptedException {
        concurrency();
        serial();
    }

    private static void concurrency() throws InterruptedException{
        Long start 
= System.currentTimeMillis(); Thread thread = new Thread(new Runnable() { @Override public void run() { int a = 0; for (long i = 0; i < count; i++) { a += 5; } } }); thread.start();
int b = 0; for (long i = 0; i < count; i++) { b--; } thread.join(); long time = System.currentTimeMillis() - start; System.out.println("concurrency:" + time + "ms, b = " + b); } private static void serial(){ long start = System.currentTimeMillis();
int a = 0; for (long i = 0; i < count; i++) { a += 5; } int b = 0; for (long i = 0; i < count; i++) { b--; } long time = System.currentTimeMillis() - start; System.out.println("serial:" + time + "ms, b = " + b + ", a=" + a); } }

輸出

技術分享圖片

答案是並不一定,當測試量達到一百萬的時候,並發才能比串行優勢點(本代碼環境結果);

線程創建和上下文切換都是需要開銷的。

如何減少上下文的切換?

  • 無鎖並發編程:可以使用一些方法避免使用鎖,如將數據的ID按照Hash算法取模分段,不同的線程處理不同段的數據。
  • CAS算法:Java的Atomic包使用CAS算法更新數據,而不需要加鎖。
  • 使用最少線程:避免創建不需要的線程。
  • 協程:在單線程裏實現多任務的調度,並在單線程裏維持多個任務間的切換。

挑戰二:死鎖

public class DeadLockDemo {
    
    private static String A = "A";
    private static String B = "B";

    public static void main(String[] args) {
        new DeadLockDemo().deadLock();
    }
    
    private void deadLock(){
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (A){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    synchronized (B){
                        System.out.println("1");
                    }
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (B){
                    synchronized (A){
                        System.out.println("2");
                    }
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

避免死鎖的幾個常見方法:

  • 避免一個線程同時獲取多個鎖
  • 避免一個線程在鎖內同時占用多個資源,盡量保證每個鎖只占一個資源
  • 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內部鎖機制
  • 對於數據庫鎖,加鎖和解鎖必須在一個數據庫連接裏

挑戰三:資源限制的挑戰

(1) 什麽是資源限制?

指在進行並發編程時,程序的執行速度受限於計算機硬件資源或軟件資源。

(2) 資源限制引發的問題?

在並發編程中,將代碼執行速度加快的原則是將代碼中串行的部分變成並發執行,但是如果將某段串行的代碼並發執行,因為受限於資源,仍然在串行執行,這時候程序不僅不會加快執行,反而會更慢,因為增加了上下文切換和資源調度的時間。

(3)如何解決資源限制的問題?

對於硬件限制,可以考慮集群並行執行程序。既然單機的資源有限制,就讓程序在多機上運行。比如使用ODPS、Hadoop或者自己搭建服務器集群;

對於軟件限制,可以考慮使用資源池將資源復用。比如使用連接池將數據庫和Socket連接復用,或者在調用對方webService接口獲取數據時,只建立一個連接。

(4)如何在資源限制的情況下進行並發編程?

根據不同的資源限制調整程序的並發度,比如下載文件程序依賴於兩個資源——寬帶和硬盤讀寫速度。有數據庫操作時,設計數據庫連接數,如果SQL語句執行非常快,而線程的數量比數據庫連接數大很多,則某些線程會被阻塞,等待數據庫連接。

第一章 並發編程的挑戰