java語言裏的線程本質上就是操做系統的線程,他們是一 一對應的java
Java語言中線程共有六種狀態,分別是:算法
BLOCKED,WAITING,TIMED_WAITING 是上面提到的休眠狀態。Java線程處於這些狀態那麼這個線程就永遠沒有CPU的使用權。編程
線程能夠經過isInterrupted()方法,檢 測是否是本身被中斷了。安全
Java剛建立出來的Thread對象就是NEW狀態,而建立Thread對象主要有兩種方法。一種是繼承Thread對 象,重寫run()方法。示例代碼以下:多線程
方式一: 繼承Thread併發
// ⾃定義線程對象 class MyThread extends Thread{ public void run() { // 線程須要執⾏的代碼 } } // 建立線程對象 MyThread myThread = new MyThread();
方式二: 實現Runnable接口異步
//實現Runnable接⼝ class Runner implements Runnable { @Override public void run(){ //線程須要執⾏的代碼 } } //建立線程對象 Thread thread = new Thread(new Runner());
方式三:實現Callable接口編程語言
//實現Runnable接⼝ class Runner implements Callable<String> { @Override public String call() throws Exception { //線程須要執⾏的代碼 return null; } } //建立 FutureTask FutureTask<String> ft1 = new FutureTask(new Runner()); //執行這個任務 Thread t1 = new Thread(ft1); t1.start(); //獲取返回值 t1.get();
方法三實質上也是實現了Runnable接口,由於FutureTask實現了Runnable接口ide
Future接口提供的方法:性能
// 取消任務 boolean cancel(boolean mayInterruptIfRunning); // 判斷任務是否已取消 boolean isCancelled(); // 判斷任務是否已結束 boolean isDone(); // 得到任務執⾏結果 get(); // 得到任務執⾏結果,⽀持超時 get(long timeout, TimeUnit unit);
這兩個get()方法都是阻塞式的,若是被調用的時候,任務尚未執行完,那麼調用get()方法的線程會阻塞,直到任務執行完纔會被喚醒。
stop()會殺死線程,若是線程持有ReentrantLock鎖,被stop()的線程並不會自動調用ReentrantLock的unlock()去釋放鎖,那其餘線程就再也沒機會得到ReentrantLock鎖。因此該方法就不建議使用了,相似的方法還有suspend()和resume()方法,這兩個方法一樣也都不建議使用。
interrupt()僅僅是通知線程,線程有機會執行一些後續操做,同時也能夠無視這個通知。
提升程序的性能: 下降延遲,提升吞吐量。
提升性能的方式:1優化算法;2將硬件的性能發揮到極致
在併發編程領域,提高性能本質上就是提高硬件的利用率,具體來講就是提高I/O的利用率和CPU的利用率。
若是CPU和I/O設備的利用率都很低,那麼能夠嘗試經過增長線程提升吞吐量。
咱們的成語通常都是CPU計算和I/O操做交叉執行的,因爲I/O設備的速度相對於CPU來講都是很慢的,因此大部分狀況下,I/O操做的執行時間相對於CPU計算來講都很是長,這種場景咱們通常都成爲I/O密集型程序和CPU密集型程序,計算最近線程數的方法是不一樣的。
對於CPU密集型的計算場景,理論上「線程的數量-CPU核數」就是最合適的。不過在工程上,線程的數量通常會設置爲"CPU核數+1" ,這樣的話,當線程由於偶爾的內存頁失效或其餘緣由致使阻塞時,這個額外的線程能夠頂上,從而保證CPU的利用率。
對於I/O密集型的計算場景,好比前面咱們的例子中,若是CPU計算和I/O操做的耗時是1:1,那麼2個線程是 最合適的。若是CPU計算和I/O操做的耗時是1:2,那多少個線程合適呢?是3個線程,以下圖所示:CPU在 A、B、C三個線程之間切換,對於線程A,當CPU從B、C切換回來時,線程A正好執行完I/O操做。這樣CPU 和I/O設備的利用率都達到了100%。
更多的精力其實應該放在算法的優化上,線程池的配置,按照經驗配置一個,隨時關注線程池大小對程序 的影響便可,具體作法:能夠爲你的程序配置一個全局的線程池,須要異步執行的任務,扔到這個全局線 程池處理,線程池大小按照經驗設置,每隔一段時間打印一下線程池的利用率,作到內心有數。
設置線程數的原則: 將硬件的性能發揮到極致。
每一個線程都有本身的調用棧,局部變量保存在線程各自的調用棧裏面,不會共享,因此天然也就沒有併發問題。
局部變量的做用域是方法內部,也就是說當方法執行完,局部變量就沒用了,局部變量和方法同生共死。
局部變量是和方法同生共死的,一個變量若是想跨越方法的邊界,就必須建立在堆裏。
兩個線程能夠同時用不一樣的參數調用相同的方法。
每一個線程都有本身獨立的調用棧。
線程封閉 : 僅在單線程內訪問數據。不存在多線程的數據共享。
由於每調用一個方法就會在棧上建立一個棧幀,方法調用結束後就會彈出該棧幀,而棧的大小不是無限的 ,因此遞歸調用次數過多的話就會致使棧溢出。而遞歸調用的特色是每遞歸一次,就要建立一個新的棧幀 ,並且還要保留以前的環境(棧幀),直到遇到結束條件。因此遞歸調用必定要明確好結束條件,不要出現死循環,並且要避免棧太深。
解決方法:
**** 碼字不易若是對你有幫助請給個關注****
**** 愛技術愛生活 QQ羣: 894109590****