java併發編程(二)java線程介紹 一、使用和創建 二、查看線程 三、線程運行原理

一、使用和創建

相信學過的同學,只需要記住如下三句就知道如何使用了:

繼承Thread類
實現Runnnable接口
實現Callable接口

但是實際使用過程中,並不需要我們的類去繼承或實現上面這幾個類或接口,下面我們看看常用方式。

1.1 Thread

直接使用Thread對象:

// 創建線程對象
Thread t = new Thread() {
  public void run() {
  // 要執行的任務
  }
};
// 啓動線程
t.start();

1.2 Runnable

使用 Runnable 配合 Thread:此種方法是將線程與任務分開的方式,其實Thread代表線程,而Runnable代表人物,要將任務放在線程當中執行.

Runnable runnable = new Runnable() {
  public void run(){
  // 要執行的任務
  }
};
// 創建線程對象
Thread t = new Thread( runnable );
// 啓動線程
t.start();

Thread與Runnable的關係如下:

Thread實現了Runnable接口,並且Runnable是有一個函數式接口(我在java8中講解了何爲函數式接口,並且如何使用),只有一個run方法。

此方式的優點:

  • 用 Runnable 更容易與線程池等高級 API 配合
  • 用 Runnable 讓任務類脫離了 Thread 繼承體系,更靈活

1.3 Callable

Callable是帶有返回值的線程使用方式,通常配合FutureTask使用。

    /**
     * FutureTask配合callable
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // 創建任務對象
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() {
                return 100;
            }
        });
        // 參數1 是任務對象; 參數2 是線程名字,推薦
        new Thread(task, "t3").start();
        // 主線程阻塞,同步等待 task 執行完畢的結果
        Integer result = task.get();

        System.out.println(result);
    }


如上的代碼可以使用lambda表達式縮減:

FutureTask<Integer> task = new FutureTask<>(() -> 100);

二、查看線程

在不同的環境上有不同的線程查看方式,如下所示:

2.1 windows

  • 任務管理器可以查看進程和線程數,也可以用來殺死進程
  • tasklist 查看進程
  • taskkill 殺死進程

2.2 linux

  • ps -ef 查看所有進程
  • ps -fT -p <PID> 查看某個進程(PID)的所有線程
  • kill 殺死進程
  • top 按大寫 H 切換是否顯示線程
  • top -H -p <PID> 查看某個進程(PID)的所有線程

2.3 java命令

  • jps 命令查看所有 Java 進程
  • jstack <PID> 查看某個 Java 進程(PID)的所有線程狀態
  • jconsole 來查看某個 Java 進程中線程的運行情況(圖形界面)

以如下方式配置jconsole:

  • 需要以如下方式運行你的 java 類

java
-Djava.rmi.server.hostname=ip地址
-Dcom.sun.management.jmxremote.port=連接端口
-Dcom.sun.management.jmxremote.ssl=是否安全連接
-Dcom.sun.management.jmxremote.authenticate=是否認證 java類

  • 修改 /etc/hosts 文件將 127.0.0.1 映射至主機名

如果要認證訪問,還需要做如下步驟

  • 複製 jmxremote.password 文件
  • 修改 jmxremote.password 和 jmxremote.access 文件的權限爲 600 即文件所有者可讀寫
  • 連接時填入 controlRole(用戶名),R&D(密碼)

三、線程運行原理

方便理解,將java的JMM(java內存模型)放在下面,但不是本章節講解的重點:

3.1 JVM之棧與棧幀

在java的內存模型當中,有一塊區域被稱爲java虛擬機棧(Java Virtual Machine Stacks)。當然還有本地方法棧,這個棧是給原生native方法使用的,本文以java虛擬機棧作爲講解。

我們都知道 JVM 中由堆、棧、方法區所組成,其中棧(java虛擬機棧)內存是給誰用的呢?其實就是線程,每個線程啓動後,虛擬機就會爲其分配一塊棧內存。

在虛擬機棧內,每個方法會生成一個棧幀。每個棧幀代表一次次的方法調用,一個方法的執行到執行完成的過程,代表棧幀從入棧到出棧的過程。

  • 每個棧由多個棧幀(Frame)組成,對應着每次方法調用時所佔用的內存。
  • 每個線程只能有一個活動棧幀,對應着當前正在執行的那個方法。

虛擬機棧會拋出StackOverflowError和OutOfMemoryError。

棧幀的組成如下所示:

3.2 線程上下文切換

線程上下文切換(Thread Context Switch):因爲以下一些原因導致 cpu 不再執行當前的線程,轉而執行另一個線程的代碼。

  • 線程的 cpu 時間片用完
  • 垃圾回收
  • 有更高優先級的線程需要運行
  • 線程自己調用了 sleep、yield、wait、join、park、synchronized、lock 等方法

當 Context Switch 發生時,需要由操作系統保存當前線程的狀態,並恢復另一個線程的狀態,Java 中對應的概念就是程序計數器(Program Counter Register),它的作用是記住下一條 jvm 指令的執行地址,是線程私有的。

  • 狀態包括:程序計數器、虛擬機棧中每個棧幀的信息,如局部變量、操作數棧、返回地址等。
  • Context Switch 頻繁發生會影響性能

本章節限於篇幅介紹到這,下一章節會重點介紹java現成的方法,狀態和特性等,有用的話給個贊吧。

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