java併發編程 | 線程詳解
進程與線程
進程:操作系統在運行一個程序的時候就會爲其創建一個進程(比如一個java程序),進程是資源分配的最小單位,一個進程包含多個線程
線程:線程是cpu調度的最小單位,每個線程擁有各自的計數器,對戰和局部變量等屬性,並且能過訪問共享的內存變量
線程的狀態
java線程的生命週期總共包括6個階段:
初始狀態:線程被創建,但是還沒有調用start()方法
運行狀態:java中將就緒狀態和運行狀態統稱爲運行狀態
阻塞狀態:線程阻塞,線程等待進入synchronized修飾的代碼塊或方法
等待狀態:線程進入等待狀態,需要調用notify()或notifyAll()進行喚醒
超時等待狀態:線程進入等待狀態,在指定時間後自行返回
終止狀態:線程執行完畢
在某一時刻,線程只能處於其中的一個狀態
線程初始化後,調用start()方法變爲運行狀態,調用wait(),join()等方法,線程由運行狀態變爲等待狀態,調用notify()或notifyAll()等方法,線程由等待狀態變成運行狀態,超時等待狀態就是在等待狀態基礎上加了時間限制,超過規定時間,自動更改爲運行狀態,當需要執行同步方法時,如果沒有獲得鎖,這時線程狀態就變爲阻塞狀態,直到獲取到鎖,變爲運行狀態,當執行完線程的run()方法後,線程變爲終止狀態
創建線程
創建線程有三種方式
繼承Thread類
實現Runnable接口
實現Callable接口
繼承Thread類
/**
- @author: chenmingyu
- @date: 2019/4/8 15:13
- @description: 繼承Thread類
*/
public class ThreadTest extends Thread{
@Override
public void run() {
IntStream.range(0,10).forEach(i->{
System.out.println(this.getName()+":"+i);
});
}
public static void main(String[] args) {
Thread thread = new ThreadTest();
thread.start();
}
}
實現Runnable接口
/**
- @author: chenmingyu
- @date: 2019/4/8 15:18
- @description: 實現Runnable接口
*/
public class RunnableTest implements Runnable {
@Override
public void run() {
IntStream.range(0,10).forEach(i->{
System.out.println(Thread.currentThread().getName()+":"+i);
});
}
public static void main(String[] args) {
Runnable runnable = new RunnableTest();
new Thread(runnable,"RunnableTest").start();
}
}
實現Callable接口
/**
- @author: chenmingyu
- @date: 2019/4/8 15:23
- @description: 實現Callable接口
*/
public class CallableTest implements Callable {
@Override
public Integer call() throws Exception {
IntStream.range(0,10).forEach(i->{
System.out.println(Thread.currentThread().getName()+":"+i);
});
return -1;
}
public static void main(String[] args) throws Exception {
Callable callable = new CallableTest();
FutureTask futureTask = new FutureTask(callable);
new Thread(futureTask,"future").start();
System.out.println("result:"+futureTask.get());
}
}
線程的暫停,恢復,停止
不安全的線程暫停,恢復,停止操作
Thread提供的過期方法可以實現對線程進行暫停suspend(),恢復resume(),停止stop()的操作
例:創建一個線程,run()中循環輸出當前時間,在main()方法中對新建線程進行暫停,恢復,停止的操作
/**
- @author: chenmingyu
- @date: 2019/4/8 15:51
- @description: 線程的暫停,恢復,停止
*/
public class OperationThread implements Runnable{
@Override
public void run() {
while (true){
try {
TimeUnit.SECONDS.sleep(1L);
System.out.println(Thread.currentThread().getName()+"運行中:"+LocalTime.now());
}catch (InterruptedException e){
System.err.println(e.getMessage());
}
}
}
public static void main(String[] args) throws Exception{
Runnable runnable = new OperationThread();
Thread thread = new Thread(runnable,"operationThread");
/**
* 啓動,輸出當前時間
*/
thread.start();
TimeUnit.SECONDS.sleep(3L);
/**
* 線程暫停,不在輸出當前時間
*/
System.out.println("此處暫停:"+LocalTime.now());
thread.suspend();
TimeUnit.SECONDS.sleep(3L);
/**
* 線程恢復,繼續輸出當前時間
*/
System.out.println("此處恢復:"+LocalTime.now());
thread.resume();
TimeUnit.SECONDS.sleep(3L);
/**
* 線程停止,不在輸出當前時間
*/
thread.stop();
System.out.println("此處停止:"+LocalTime.now());
TimeUnit.SECONDS.sleep(3L);
}
}
輸出
因爲是過期方法,所以不推薦使用,使用suspend()方法後,線程不會釋放已經佔有的資源,就進入睡眠狀態,容易引發死鎖問題,而使用stop()方法終結一個線程是不會保證線程的資源正常釋放的,可能會導致程序異常
安全的線程暫停,恢復,停止操作
線程安全的暫停,恢復操作可以使用等待/通知機制代替,安全的停止操作可以用線程是否被中斷進行判斷
安全的線程暫停,恢復(等待/通知機制)
相關方法:
方法名 描述
notify() 通知一個在對象上等待的線程,使其重wait()方法中返回,前提是該線程獲得了對象的鎖
notifyAll() 通知所有等待在該對象上的線程
wait() 調用該方法線程進入等待狀態,只有等待另外線程的通知或被中斷纔會返回,調用該方法會釋放對象的鎖
wait(long) 超時等待一段時間(毫秒),如果超過時間就返回
wait(long,int) 對於超時時間耕細粒度的控制,可以達到納秒
例:創建一個名爲waitThread的線程,在run()方法,使用中使用synchronized進行加鎖,以變量flag爲條件進行while循環,在循環中調用LOCK.wait()方法,此時會釋放對象鎖,由main()方法獲得鎖,調用LOCK.notify()方法通知LOCK對象上等待的waitThread線程,將其置爲阻塞狀態,並將變量flag置爲true,當waitThread線程再次獲取對象鎖之後繼續執行餘下代碼
/**
- @author: chenmingyu
- @date: 2019/4/8 20:00
- @description: wait/notify
*/
public class WaitNotifyTest {
private static Object LOCK = new Object();
private static Boolean FLAG = Boolean.TRUE;
public static void main(String[] args) throws InterruptedException{
Runnable r = new WaitThread();
new Thread(r,"waitThread").start();
TimeUnit.SECONDS.sleep(1L);
synchronized (LOCK){
System.out.println(Thread.currentThread().getName()+"喚醒waitThread線程:"+LocalTime.now());
/**
* 線程狀態由等待狀態變爲阻塞狀態
*/
LOCK.notify();
/**
* 只有當前線程釋放對象鎖,waitThread獲取到LOCK對象的鎖之後纔會從wait()方法中返回
*/
TimeUnit.SECONDS.sleep(2L);
FLAG = Boolean.FALSE;
}
}
public static class WaitThread implements Runnable {
@Override
public void run() {
/**
* 加鎖
*/
synchronized (LOCK){
while (FLAG){
try {
System.out.println(Thread.currentThread().getName()+"運行中:"+LocalTime.now());
/**
* 線程狀態變爲等待狀態
*/
LOCK.wait();
/**
* 再次獲得對象鎖之後,纔會執行
*/
System.out.println(Thread.currentThread().getName()+"被喚醒:"+LocalTime.now());
}catch (InterruptedException e){
System.err.println(e.getMessage());
}
}
}
System.out.println(Thread.currentThread().getName()+"即將停止:"+LocalTime.now());
}
}
}
輸出
可以看到在mian線程調用LOCK.notify()方法後,沉睡了2s才釋放對象鎖,waitThread線程在獲得對象鎖之後執行餘下代碼
安全的線程停止操作(中斷標識)
線程的安全停止操作是利用線程的中斷標識來實現,線程的中斷屬性表示一個運行彙總的線程是否被其他線程進行了中斷操作,其他線程通過調用該線程的interrupt()方法對其進行中斷操作,而該線程通過檢查自身是否被中斷來進行響應,當一個線程被中斷可以使用Thread.interrupted()方法對當前線程的中斷標識位進行復位
例:新建一個線程,run方法中使用Thread.currentThread().isInterrupted()是否中斷作爲判斷條件,在主線程中使用thread.interrupt()方法對子線程進行中斷操作,用來達到終止線程的操作,這種方式會讓子線程可以去清理資源或一些別的操作,而使用stop()方法則會會直接終止線程
/**
- @author: chenmingyu
- @date: 2019/4/8 20:47
- @description: 中斷
*/
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Runnable r = new StopThread();
Thread thread = new Thread(r,"stopThread");
thread.start();
TimeUnit.SECONDS.sleep(1L);
System.out.println(Thread.currentThread().getName()+"對stopThread線程進行中斷:"+LocalTime.now());
thread.interrupt();
}
public static class StopThread implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"運行中:"+LocalTime.now());
}
System.out.println(Thread.currentThread().getName()+"停止:"+LocalTime.now());
}
}
}
未完待續...
原文地址https://www.cnblogs.com/cmyxn/p/10673601.html