java併發編程的藝術-第四章之Java併發基礎

爲何要使用多線程

Java天生就是多線程程序,簡單的一個main() 方法的執行,實際上是由main線程和其它的線程共同執行的。固然使用多線程有以下的好處:java

        一、利用更多的處理器核心編程

        二、更快的響應速度,如將數據一致性不強的操做交給其它的線程去操做多線程

        三、更好的編程模型ide

線程簡介

1、線程優先級spa

在線程執行過程當中,線程優先級決定了須要分配處理器資源的多少,從而決定了獲取時間片的多少。如今線程構建時,能夠經過setPriority()方法來修改優先級,默認優先級是5.可是在不一樣的JVM以及操做系統上面,線程的規劃有些不一樣,甚至有些操做系統會忽略對線程優先級的設定,都是採用默認值。操作系統

 

2、線程狀態線程

線程狀態code

狀態名稱 說明
NEW 初始狀態,線程被建立,尚未有調用start()方法
RUNNABLE 運行狀態,java中就緒也屬於運行狀態
BLOCKED 阻塞狀態,表示線程阻塞在進入synchronize修飾的方法或代碼塊(獲取鎖)時的狀態
WAITING 等待狀態,進入該狀態表示當前線程須要等待其餘的線程作出特定動做(通知、中斷)才返回
TIME_WAITING 超時等待,不一樣於WAITING,它是等到指定時間到達自動返回
TERMINTED 終止狀態,表示當前線程執行完成

 

Daemon 是一種支持線程,主要用來支持程序的後臺的調度,因此當Java虛擬機中不存在非Daemon線程(main 線程)時,Java虛擬機將會退出。orm

import java.util.concurrent.TimeUnit;  
  
/** 
 * Created by shidong.wu on 2017/10/13. 
 */  
public class Daemon {  
    public static void main(String[] args) {  
        Thread thread = new Thread(new DaemonRunner(),"DaemonRunner");  
        thread.setDaemon(true);  
        thread.start();  
    }  
    static class DaemonRunner implements Runnable {  
        @Override  
        public void run() {  
            try {  
                TimeUnit.SECONDS.sleep(10);  
            }catch (InterruptedException e) {  
  
            }finally {  
                System.out.println("Daemon finally");  
            }  
        }  
    }  
}

運行上面的程序終端沒有任何輸出,主要是由於非Daemon線程(main線程)運行結束後,Java虛擬機退出了,因此DaemRunner中的finally代碼塊沒有執行。對象

 

3、中斷

中斷能夠理解爲線程的一個標識位屬性,它標識一個線程是否其它的線程進行的了中斷操做。其它的線程經過調用當前線程的interrupt()方法對其進行中斷操做。線程經過方法isInterrupt()方法來檢查自身是否被中斷來進行響應。拋出InterruptException的線程的,他的中斷標識會被清除。

 

線程間通訊

1、volatile 和synchronized 關鍵字

在前面的已經講過它們的內存含義了,

一、關鍵字volatile用來修飾一個字段(變量),就是告訴程序任意一個線程,對該變量的讀取都要去共享內存中獲取,而都變量的修改必需要同步刷新到共享內存裏,volatile保證了全部線程的對變量的訪問可見性。可是過多的使用volatile,會影響程序的執行效率。

        二、關鍵字synchronized用來修飾方法和代碼塊,它主要保證多個線程在同一時刻,只能有一個線程處於synchronized修飾的方法和代碼塊中,其本質是對一個對象監視器(Monitor)的獲取,這個獲取的過程是排他的,同一個時刻,只能有一個線程能獲取大對象的監視器。沒有獲取到監視器的線程進入同步隊列,等待其餘線程釋放鎖。以下圖:

 

2、等待/通知機制

等待 / 通知機制是指一個線程A調用了對象O的wait()方法,釋放鎖,進入等待狀態,而線程B調用了對象O的notify()或者notifyAll()方法,線程A收到通知後從對象O的wait()方法返回,進而執行後面的操做。

等待 / 通知機制的經典範式,分爲,消費者和生產者;

消費者遵循的規範:

一、獲取對象的鎖

二、若是條件不知足,調用對象的wait()方法,被通知後還要進行條檢查

三、條件知足後執行對應的邏輯

對應的僞代碼:

synchronized(對象){

while(條件不知足){

對象.wait();

   }

對應的邏輯;

}

 

 

生產者遵循的規範:

一、獲取對象的鎖

二、改變條件

三、通知全部等待在該對象的線程

對此的僞代碼:

synchronized (對象) {

改變條件;

       對象.notifyAll();

 

 

}

 

下面咱們就利用上面的等待 / 通知的範式實現一個線程的等待/ 通知的機制;

package com.concurrency;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created by shidong.wu on 2017/10/14.
 */
public class WaitNotify {

    private static boolean flag = true;

     private  static Object lock = new Object();

    public static void main(String[] args) throws Exception{
        Thread waitThread = new Thread(new Wait(),"waitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);

        Thread notifyThread = new Thread(new Notify(),"notifyThread");
        notifyThread.start();
    }


    static class Wait implements Runnable {
        public void run() {
            // 加鎖,lock對象加鎖
            synchronized (lock) {
                while (flag){  // 條件不知足
                    try {
                        lock.wait();
                    }catch (InterruptedException e){
                    }
                }
                // 條件知足時,進行完成操做
                System.out.println(Thread.currentThread() + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

    static class Notify implements Runnable {
        public void run() {
            // 加鎖,lock對象加鎖
            synchronized (lock) {
                lock.notifyAll(); // 通知
                flag = false;

            }
        }
    }
}


3、join() 使用

若是一個線程A調用了thread.join()表示線程A等待線程thread線程終止以後,才從threa.join()處返回。

 

4、ThreadLocal 使用

ThreadLocal 是一個線程變量,這個變量是附帶在線程上面的。

相關文章
相關標籤/搜索