java-學習篇-併發編程基礎

進程

進程是系統進行資源分配和調度的一個獨立單位。當一個程序進入內存運行時,即變成一個進程。
特徵:獨立性、動態性(程序只是一個靜態的指令集合,而進程是一個正在系統中活動的指令集合)、併發性。
進程是在一個上下文的執行環境中運行的,這個執行環境稱爲進程的映像,或稱進程圖像。進程的存儲映像組成部分:進程控制塊(PCB,即進程狀態信息)、進程執行的程序、執行時所需要的數據和進程執行時使用的工作區。

併發性:同一時刻只能有一條指令執行,但是多個進程指令被快速輪換執行。現代操作系統多進程的併發:共用式的多任務操作策略;搶佔式多任務操作策略。
並行性:指在同一時刻,有多條指令在多個處理器上同時執行。

線程

線程是進程的執行單元,線程可以擁有自己的堆棧、自己的程序計數器和自己的局部變量,但不擁有系統資源,與父進程的其他線程共享該進程所擁有的全部資源。線程是獨立運行的,它並不知道進程中是否還有其他線程存在。線程的執行是搶佔式的,當前運行的線程在任何時候都可能被掛起,以便另外一個線程可以運行。一個線程可以創建和撤銷另一個線程。

操作系統可以同時執行多個任務,每個任務就是進程;進程可以同時執行多個任務,每個任務就是線程。

線程的風險

  • 安全性問題(永遠不發生糟糕的事情,核心是正確性):由於多個線程要共享相同的內存地址空間,並且是併發運行,因此它們可能會訪問或修改其他線程正在使用的變量。
  • 活躍性問題(某件正確的事情最終會發生):當某個操作無法繼續執行下去時,就會發生活躍性問題。(死循環、死鎖、飢餓等)
  • 性能問題(正確的事情儘快發生):CPU時間將更多地花在線程調度而不是線程運行上。

線程的創建與啓動

Thread與Runnable

package java.lang;
public class Thread implements Runnable

Thread.currentThread();// 返回當前正在執行的線程對象
public final String getName()// 返回調用該方法的線程名字
public synchronized void start()

// 1.繼承Thread類來創建線程類,重寫run()方法,方法體就是線程執行體,調用start()方法開啓線程
// 2.創建Runnable接口的實現類,new Thread(Runnable target)創建Thread對象,調用start()方法開啓線程

Callable和Future

// Java 5開始,提供Callable和Future接口,Future實現類FutureTask
// Callable(函數式接口)提供一個call()方法可以作爲線程執行體,call()方法可以有返回值
package java.util.concurrent;
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

// 3.創建Callable對象

線程生命週期

啓動線程使用start()方法,而不是run()方法!如果直接調用線程對象的run()方法,系統把線程對象當成一個普通對象,而run()方法也是一個普通方法,而不是線程執行體。

  • 新建(new)
  • 就緒(Runnable):調用了start()方法之後,該線程處於就緒狀態,Java虛擬機會爲其創建方法調用棧和程序計數器,處於這個狀態中的線程並沒有開始運行,只是表示該線程可以運行了。至於該線程何時開始運行,取決於JVM裏線程調度器的調度。
  • 運行(Running)
  • 阻塞(Blocked)
  • 死亡(Dead)

相關方法介紹:

  • isAlive():線程處於就緒、運行、阻塞三種狀態時,返回true
  • stop():該方法容易導致死鎖,通常不推薦使用。已廢棄

控制線程

join()
當在某個程序執行流中調用其他線程的join()方法時,調用線程將被阻塞,直到被join()方法加入的join線程執行完爲止。

sleep()
讓當前正在執行的線程暫停一段時間,並進入阻塞狀態。

yield()
讓當前正在執行的線程暫停,但它不會阻塞該線程,它只是將該線程轉入就緒狀態。

sleep()和yield()區別:

  • sleep()方法暫停線程後,會給其他線程執行機會,不會理會其他線程的優先級;yield()方法只會給優先級相同,或優先級更高的線程執行機會。
  • sleep()方法將線程轉入阻塞狀態;而yield()不會將線程轉入阻塞狀態,只是強制當前線程進入就緒狀態。

setDaemon(true)
設置後臺線程(Daemon Thread,守護線程)。如果所有的前臺線程都死亡,後臺線程會自動死亡。JVM的垃圾回收線程就是典型的後臺線程。

setPriority
設置線程優先級,範圍是1~10之間。

  • MIN_PRIORITY:1
  • NORM_PRIORITY:5,默認值
  • MAX_PRIORITY:10

線程同步

  • synchronized:同步鎖。同步代碼塊、同步方法。程序執行wait()方法,則當前線程暫停,並釋放同步監聽器;調用Thread.sleep()、Thread.yield()方法來暫停當前線程的執行,當前線程不會釋放同步監聽器
  • Lock顯式定義同步鎖。
package java.util.concurrent.locks;
Lock、ReadWriteLock(讀寫鎖)是java 5 提供的兩個根接口。
StampedLock java 8 新增類,在大多數場景中可以替代傳統的 ReentrantReadWriteLock。

// 可重入鎖(Lock實現類)
ReentrantLock

// ReadWriteLock 實現類
ReentrantReadWriteLock
class MyLock {
    private final ReentrantLock lock = new ReentrantLock();

    public void methodA() {
        lock.lock();
        try {
            ...
        } finally {
            lock.unlock();
        }
    }
}
  • 死鎖:當兩個線程相互等待對方釋放同步監聽器時就會發生死鎖。

線程通信

  • 等待/通知機制:Object類提供的wait()、notify()、notifyAll()。由同步監聽器對象來調用。
  • 使用Lock對象來保證同步時,系統中不存在隱式的同步監聽器,也就不能使用等待/通知方法進行線程通信。調用Lock對象的newCondition()方法,將Condition實例綁定在一個Lock對象上。
package java.util.concurrent.locks;
public interface Condition 

await()
signal()
signalAll()
  • 管道輸入/輸出流:主要用於線程之間的數據傳輸,而傳輸的媒介爲內存。
  • 使用阻塞隊列(BlockingQueue):主要用途不是作爲容器,而是作爲線程同步的工具。

線程組

package java.lang;
public class ThreadGroup implements Thread.UncaughtExceptionHandler

線程池

java 5 開始,java內建支持線程池。

package java.util.concurrent;
public class Executors

// 可重用的、具有固定線程數的線程池
ExecutorService newFixedThreadPool(int nThreads)
// 可以在指定延遲後執行線程任務
ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
// java 8 新增方法
ExecutorService newWorkStealingPool(int parallelism)
ExecutorService newWorkStealingPool()
package java.util.concurrent;
public interface ExecutorService extends Executor

submit()
shutdown()

// 實現類
ThreadPoolExecutor

java 7 提供了 ForkJoinPool,支持將一個任務拆分成多個“小任務”並行計算,再把多個“小任務”的結果合併成總的計算結果。

線程相關類

  • ThreadLocal
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章