Java 多線程

em,據說多線程是面試的重點。第一次學習的時候,只是粗略的看了一下,不少概念並非很清楚。此次整理一下,省得白學java

線程與進程

進程面試

  • 是指一個內存中運行的應用程序,每一個進程都有一個獨立的內存空間

線程緩存

  • 是進程中的一個執行路徑,共享一個內存空間,線程之間能夠自由切換,併發執行. 一個進程最少
    有一個線程
  • 線程其實是在進程基礎之上的進一步劃分,一個進程啓動以後,裏面的若干執行路徑又能夠劃分
    成若干個線程

線程調度

分時調度安全

  • 全部線程輪流使用 CPU 的使用權,平均分配每一個線程佔用 CPU 的時間。

搶佔式調度多線程

  • 優先讓優先級高的線程使用 CPU,若是線程的優先級相同,那麼會隨機選擇一個(線程隨機性),
    Java使用的爲
    搶佔式調度。
  • CPU使用搶佔式調度模式在多個線程間進行着高速的切換。對於CPU的一個核新而言,某個時刻,
    只能執行一個線程,而 CPU的在多個線程間切換速度相對咱們的感受要快,看上去就是 在同一時
    刻運行。 其實,多線程程序並不能提升程序的運行速度,但可以提升程序運行效率,讓CPU的 使
    用率更高。

同步與異步

同步: 排隊執行 , 效率低可是安全.
異步: 同時執行 , 效率高可是數據不安全.併發

併發與並行

併發:指兩個或多個事件在同一個時間段內發生。
並行:指兩個或多個事件在同一時刻發生(同時發生)。異步

建立線程的經常使用方法

繼承Thread類:ide

public class ThreadDemo extends Thread{

    public ThreadDemo(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Hello");
    }

    public static void main(String[] args) {
        new ThreadDemo("自定義線程").start();
        System.out.println(Thread.currentThread().getName() + " Hello");
    }
}

實現Runnable接口學習

public class RunnableDemo implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Hello");
    }

    public static void main(String[] args) {
        new Thread(new RunnableDemo(), "自定義線程").start();
        System.out.println(Thread.currentThread().getName() + " Hello");
    }
}

實現Runnable 與 集成Thread相比有以下優點:線程

  1. 經過建立任務,給線程分配任務實現多線程,更適合多線程同時執行相同的任務狀況
    2. 避免單繼承帶來的侷限性
    3. 任務與線程自己是分離的,提升了程序的健壯性
    4. 後續學習的線程技術,只接受Runnable類型的任務,不接受Thread類型的線程

線程安全問題

當多個線程操做同一個數據的時候會出現線程安全問題

public class Demo04 {
    /**
     * 線程安全問題
     *
     * @param args
     */
    private static int count = 100;

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (count > 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + "當前票數爲:" + count);
                    count--;
                    System.out.println(Thread.currentThread().getName() + ":" + "當前票數爲:" + count);
                }
            }
        };

        new Thread(runnable, "線程一").start();
        new Thread(runnable, "線程二").start();
        new Thread(runnable, "線程三").start();
        new Thread(runnable, "線程四").start();
    }
}

線程二:當前票數爲:100
線程三:當前票數爲:100
線程三:當前票數爲:98
線程一:當前票數爲:100
線程一:當前票數爲:97
...
線程三:當前票數爲:4
線程一:當前票數爲:5
線程一:當前票數爲:-2
線程三:當前票數爲:-1
線程二:當前票數爲:0

線程安全問題解決方案

  • 解決方案一 同步代碼塊
public class 同步代碼塊 {

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            //隱式鎖
            //鎖對象
            private Object o = new Object();
            private int count = 100;

            @Override
            public void run() {
                while (true) {
                    synchronized (o) {
                        if (count > 0) {
                            System.out.println(Thread.currentThread().getName() + " count = " + count);
                            count--;
                        } else {
                            break;
                        }
                    }
                }
            }
        };

        new Thread(runnable, "線程一").start();
        new Thread(runnable, "線程二").start();
        new Thread(runnable, "線程三").start();
    }
}
  • 解決方案一 同步方法
public class 同步方法 {

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            private int count = 100;

            @Override
            public void run() {
                while (true) {
                    if (!reduce()){
                        break;
                    }
                }
            }

            public synchronized boolean reduce() {
                if (count > 0) {
                    System.out.println(Thread.currentThread().getName() + " count = " + count);
                    count--;
                    return true;
                }
                return false;
            }
        };

        new Thread(runnable, "線程一").start();
        new Thread(runnable, "線程二").start();
        new Thread(runnable, "線程三").start();
    }
}

線程一 count = 100
線程一 count = 99
線程一 count = 98
線程一 count = 97
...
線程一 count = 87
線程一 count = 86
...
線程一 count = 4
線程一 count = 3
線程一 count = 2
線程一 count = 1

多線程的各類鎖

  • 隱式鎖(同步代碼塊,同步方法)
  • 顯示鎖
public class 顯示鎖 {

    /**
     * 顯示鎖: Lock 子類 ReentrantLock
     *
     * @param args
     */
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            private int count = 100;
            private Lock lock = new ReentrantLock();

            @Override
            public void run() {
                while (true) {
                    lock.lock();
                    if (count > 0) {
                        System.out.println(Thread.currentThread().getName() + " count = " + count);
                        count--;
                    } else {
                        break;
                    }
                    lock.unlock();
                }
                System.out.println("bye");
            }
        };

        new Thread(runnable, "線程一").start();
        new Thread(runnable, "線程二").start();
        new Thread(runnable, "線程三").start();
    }
}
  • 公平鎖和非公平鎖
  • 線程死鎖

線程的七種狀態

  • born(新生狀態)
  • runnable(就緒狀態)
  • running(運行狀態)
  • waiting(等待狀態)
  • sleeping(睡眠狀態)
  • blocked(阻塞狀態)
  • dead(死亡狀態)

Callable

//接口定義 
//Callable接口 
public interface Callable<V> {
	V call() throws Exception; 
}
//Runnable接口 
public interface Runnable { 
	public abstract void run(); 
}

線程池 Executors

若是併發的線程數量不少,而且每一個線程都是執行一個時間很短的任務就結束了,這樣頻繁建立線程 就會大大下降 系統的 效率,由於頻繁建立線程和銷燬線程須要時間. 線程池就是一個容納多個線程的容 器,池中的線程能夠反覆使用,省去了頻繁建立線程對象的操做,節省了大量的時間和資源。

線程池的好處

  • 下降資源消耗。
  • 提升響應速度。
  • 提升線程的可管理性。

Java中的四種線程池 . ExecutorService

  1. 緩存線程池
public class 緩存線程池 {
    /**
     *  緩存線程池.
     *  (長度無限制)
     *  執行流程:
     *  1. 判斷線程池是否存在空閒線程
     *  2. 存在則使用
     *  3. 不存在,則建立線程 並放入線程池, 而後使用
     */
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Java");
            }
        });

        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Java");
            }
        });

        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Java");
            }
        });
    }
}

pool-1-thread-1 Java
pool-1-thread-2 Java
pool-1-thread-2 Java

  1. 定長線程池
public class 定長線程池 {

    /** 定長線程池.
     *  (長度是指定的數值)
     *  執行流程:
     * 1. 判斷線程池是否存在空閒線程
     * 2. 存在則使用
     * 3. 不存在空閒線程,且線程池未滿的狀況下,則建立線程 並放入線程池, 而後使用
     * 4. 不存在空閒線程,且線程池已滿的狀況下,則等待線程池存在空閒線程
     */
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);

        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Java");
            }
        });

        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Java");
            }
        });


        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Java");
            }
        });

        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " Java");
            }
        });
    }
}

pool-1-thread-1 Java
pool-1-thread-2 Java
pool-1-thread-1 Java
pool-1-thread-2 Java

  1. 單線程線程池
    效果與定長線程池 建立時傳入數值1 效果一致.
  2. 週期性任務定長線程池
相關文章
相關標籤/搜索