Java 中建立線程的方法有三種:bash
1. 繼承 Thread 類建立線程多線程
新建一個類繼承 Thread 類,並重寫 Thread 類的 run() 方法。 建立 Thread 子類的實例。 調用該子類實例的 start() 方法啓動該線程。ide
代碼舉例以下:測試
public class HelloThread1 {
static class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println("Hello Thread");
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
}
}
複製代碼
2.實現 Runnable 接口建立線程:ui
建立一個類實現 Runnable 接口,並重寫該接口的 run() 方法。 建立該實現類的實例。 將該實例傳入 Thread(Runnable r) 構造方法中建立 Thread 實例。 調用該 Thread 線程對象的 start() 方法。this
代碼舉例以下:spa
public class HelloThread1 {
static class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println("Hello Thread");
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
}
}
複製代碼
3. 使用 Callable 和 FutureTask 建立線程:線程
建立一個類實現 Callable 接口,並重寫 call() 方法。 建立該 Callable 接口實現類的實例。 將 Callable 的實現類實例傳入 FutureTask(Callable callable) 構造方法中建立 FutureTask 實例。 將 FutureTask 實例傳入 Thread(Runnable r) 構造方法中建立 Thread 實例。 調用該 Thread 線程對象的 start() 方法。 調用 FutureTask 實例對象的 get() 方法獲取返回值。code
代碼舉例以下:cdn
public class HelloThread3 {
static class ThreadDemo implements Callable<String> {
@Override
public String call() {
System.out.println("Hello Thread");
return "Callable return value";
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
FutureTask<String> futureTask = new FutureTask<String>(threadDemo);
Thread thread = new Thread(futureTask);
thread.start();
try {
System.out.println(futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製代碼
關於Java中線程的生命週期,首先看一下下面這張圖:
上圖中基本上囊括了Java中多線程各重要知識點。掌握了上圖中的各知識點,Java中的多線程也就基本上掌握了。
新建狀態(New):當線程對象對建立後,即進入了新建狀態,如:Thread t = new MyThread();
就緒狀態(Runnable):當調用方法t.start()時,線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經作好了準備,隨時等待CPU調度執行,並非說執行了t.start()此線程當即就會執行;一樣還有幾種狀況會進行就緒狀態,請參見上圖。
運行狀態(Running):當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就緒狀態是進入到運行狀態的惟一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;
阻塞狀態(Blocked):處於運行狀態中的線程因爲某種緣由,暫時放棄對CPU的使用權,中止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。根據阻塞產生的緣由不一樣,阻塞狀態又能夠分爲三種:請參見上圖。
死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
sleep:使當前線程休眠指定毫秒
yield:使當前線程讓出CPU,從運行狀態轉到可運行狀態。注意僅僅是讓出,讓出以後也會加入到搶佔資源的隊伍中。
join:把指定的線程加入到當前線程,好比在線程B中調用了線程A的Join()方法,直到線程A執行完畢後,纔會繼續執行線程B
線程中止:
Thread自己提供了一個stop方法,可是這個不推薦使用。由於使用stop的時候會暴力終止線程,從而形成數據不一致。
優雅的中止線程的代碼舉例以下:
public class StopThread {
static class ThreadDemo extends Thread {
volatile boolean stopMe = false;
public void stopMe(){
this.stopMe=true;
}
@Override
public void run() {
while (true) {
if (stopMe) {
System.out.println("程序結束");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
threadDemo.stopMe();
}
}
複製代碼
以一個volatile修飾的變量stopMe來控制線程的中止。
線程中斷:
線程中斷的相關方法分別是這三個
public void interrupt() ; //中斷線程
public boolean isInterrupted(); //判斷線程是否被中斷
public static boolean interrupted(); //判斷線程是否被中斷,並清除當前中斷狀態
複製代碼
線程中斷的代碼舉例以下:
public class InterruptThread {
static class ThreadDemo extends Thread {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("程序結束");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
threadDemo.interrupt();
}
}
複製代碼
這兩個方法是JDK爲了支持多線程之間的協做而提供的。
當在線程A中調用了obj.wait()方法時,線程A會中止執行進入等待狀態。直到其餘線程調用obj.notify()時纔會進入阻塞狀態繼而等待獲取鎖。
請看下方示例代碼
package cn.shiyujun.thread.hellothread;
/**
* d
*
* @author syj
* CreateTime 2019/03/19
* describe:WaitNotify測試demo
*/
public class WaitNotifyThread {
public static Object obj = new Object();
static class WaitThreadDemo extends Thread {
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("WaitThread wait,time=" + System.currentTimeMillis());
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("WaitThread end,time=" + System.currentTimeMillis());
}
}
}
static class NotifyThreadDemo extends Thread {
@Override
public void run() {
synchronized (obj) {
System.out.println("NotifyThread notify,time=" + System.currentTimeMillis());
obj.notify();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("NotifyThread end,time=" + System.currentTimeMillis());
}
}
}
public static void main(String[] args) {
WaitThreadDemo waitThreadDemo = new WaitThreadDemo();
NotifyThreadDemo notifyThreadDemo = new NotifyThreadDemo();
waitThreadDemo.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
notifyThreadDemo.start();
}
}
複製代碼
在上方的代碼中,Wait線程會首先獲取到obj的鎖,當它執行wait方法時就會釋放obj的鎖並進入等待狀態。這個時候Notify線程能夠獲取到obj的鎖,而且喚醒Wait線程,可是由於此時Notify線程是睡眠了2秒鐘以後才釋放的obj的鎖,因此Wait線程獲取鎖的時候Notify線程已經執行完畢了。
此程序的運行結果:
WaitThread wait,time=1553088237753
NotifyThread notify,time=1553088237862
NotifyThread end,time=1553088239867
WaitThread end,time=1553088239867
複製代碼
它們兩個的功能是掛起線程和繼續執行,被suspen掛起的線程必須被resume喚醒才能夠繼續執行。乍看起來 能夠實現wait和notify的功能,不過我可不推薦你使用它們,和wait以後會釋放鎖不一樣,suspen掛起以後依然會持有鎖,這個可就很是危險了。
在一個系統中若是線程數量衆多而又功能比較一致,就能夠把這些線程放到一個線程組裏。
線程組示例代碼:
public class ThreadGroupDemo {
static class ThreadDemo extends Thread {
@Override
public void run() {
while (true){
System.out.println("I am "+Thread.currentThread().getThreadGroup().getName()+"-"+Thread.currentThread().getName());
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadGroup threadGroup=new ThreadGroup("groupDemo");
Thread t1=new Thread(threadGroup,new ThreadDemo(),"t1");
Thread t2=new Thread(threadGroup,new ThreadDemo(),"t2");
t1.start();
t2.start();
}
}
複製代碼
在線程的世界裏,由咱們本身建立的線程叫用戶線程。而一些系統建立的線程,如垃圾回收線程等被稱之爲守護線程,若是想要把一個用戶線程設置爲守護線程能夠在線程的調用線程的start方法前設置線程的daemon屬性爲true;
t1.setDaemon(true);
複製代碼
當一個程序中只有守護線程的時候這個程序也就要結束了。
Java中的線程能夠有本身的優先級,優先級高的線程在進行資源搶佔的時候每每會更有優點。線程飢餓現象就是因爲線程的優先級低沒法搶佔資源而引發的。
在Java中線程的優先級能夠設置的範圍是1到10之間,數字越大優先級越高。Java線程建立默認的優先級是5,咱們能夠線程start以前經過以下方式設置線程的優先級。
t1.setPriority(1);
複製代碼