1. 程式人生 > >Java 6-2:收放自如,融匯貫通,讓執行緒不再瘋癲——執行緒的阻塞和關閉

Java 6-2:收放自如,融匯貫通,讓執行緒不再瘋癲——執行緒的阻塞和關閉

本節重點說說執行緒什麼時候會阻塞,如何關閉

1 讓出時間片

Thread.yield();
通知並建議執行緒排程器,我已經做完了主要工作,時間片你可以分給別人了
即使呼叫了這個,還是可能沒有切換時間片,或者切換了,但是還是給了當前執行緒

Thread.sleep(1000);
TimeUnit.SECOND.sleep(1);
讓當前執行緒進入睡眠狀態,程式就阻塞在這裡了
這個的表現應該是比yield良好多了

但這兩個的特性,都不應該過於依賴
因為系統對時間片的劃分是不可依賴的
你的程式也不應對時間片的劃分有什麼依賴

2 任務的自我檢查

有些任務會在迴圈中檢查狀態值,如canceled之類的,會自己退出任務
但有時我們需要任務更突然的終止任務

注意:如果有標誌位canceled,isRunning等,這個一般是volatile的

private volatile boolean isFinished = false;

while(!isFinished){
    //do sth
}

如果不想用標誌位,還可以檢查是否被中斷

while(!Thread.currentThread().isInterrupted()){
    //do sth
}

3 阻塞時終止

任務除了自我檢查狀態,也可能阻塞在sleep中,此時可能也需要將其終結

執行緒的狀態:
——new:已經建立完畢,且已經start,資源分配完畢,等待分配時間片了,這個狀態只會持續很短的時間,下一步就會進入執行或者阻塞
——run:就緒狀態,只要給了時間片,就會執行,在任一時刻,thread可能執行也可能不執行
——block:阻塞狀態,程式本身能夠執行,但有個條件阻止了它執行,排程器會忽略這個執行緒,直到跳出阻塞條件,重新進入就緒狀態
——dead:run()方法返回,或者被中斷

都哪些方式可以進入block狀態:
——sleep:等時間到
——wait:等notify
——等待IO,如stream.read()
——等待synchronized鎖
——等待Lock鎖

另外,如下的大迴圈也可以interrupt
while(!Thread.currentThread().isInterrupted()){
    ThreadLocalVariableHolder.increment();
    System.out.println(this);
    Thread.yield();
}

終結情形1:

//注意isCanceled是個boolean,而且一般會需要volatile修飾
public void run(){ while(!isCanceled){ //do some seriers work } }

終結情形2:

//thread.interrupt()可以打斷
//Executors得不到thread的引用,只能通過ExecutorService.shutdownNow()來打斷
//如果能拿到Future,可以Future.cancel(true)來打斷
//exec.execute(Runnable)看來是打斷不了了,因為拿不到什麼引用
//exec.submit(),還是能打斷的,返回了Future
//本質上都是呼叫了thread.interrupt()

關於shutdown:
shutdown():拒絕再接收新的task,但已有的task會執行到terminate  
shutdownNow():禁止再接收新的task,已有task,在waiting的不會再start,已經執行的會嘗試stop掉  

未shutdown狀態:執行緒池還在執行,不管有沒有running,waiting的task,都會一直等待add新task(通過execute或者submit)  
shutdown狀態:執行完現有task,就會terminate

public void run(){
    while(!Thread.currentThread().isInterrupt()){   //或者用Thread.interrupted()判斷
        //do some seriers work
    }
}

public void run(){
    while(true){
        Thread.sleep(1000); 
        //被interrupt會丟擲異常,因為既然是阻塞,被意外終止,異常看似挺合理
        //do some seriers work
    }
}

終結情形3:終結不了的synchronized

在等待synchronized的執行緒,不可以被interrupt
但是注意,Lock可以嘗試獲取鎖,並可以指定阻塞等待鎖的時間限制

終結情形4:ReentrantLock.lockInterruptly()

ReentrantLock.lockInterruptly(),在這裡獲取不到鎖,會阻塞,但是可以被interrupt方法中斷

import java.util.concurrent.*;
import java.util.concurrent.locks.*;

class BlockedMutex {
  private Lock lock = new ReentrantLock();
  public BlockedMutex() {
    // Acquire it right away, to demonstrate interruption
    // of a task blocked on a ReentrantLock:
    lock.lock();
  }
  public void f() {
    try {
      // This will never be available to a second task
      lock.lockInterruptibly(); // Special call
      System.out.println("f()方法得到鎖了??");
    } catch(InterruptedException e) {
      System.out.println("f()方法沒得到鎖,而是被中斷了,被interrupt了");
    }
  }
}

class Blocked2 implements Runnable {
  BlockedMutex blocked = new BlockedMutex();
  public void run() {
    System.out.println("等f()拿到ReentranLock");
    blocked.f();
    System.out.println("從f()返回了");
  }
}

public class Interrupting2 {
  public static void main(String[] args) throws Exception {
    Thread t = new Thread(new Blocked2());
    t.start();
    TimeUnit.SECONDS.sleep(1);
    System.out.println("呼叫了t.interrupt()");
    t.interrupt();
  }
} /* Output:
Waiting for f() in BlockedMutex
Issuing t.interrupt()
Interrupted from lock acquisition in f()
Broken out of blocked call
*///:~

終結情形5:IO密集型阻塞

在等待InputStream.read()的執行緒,不可以被interrupt
但是有個笨辦法:關閉執行緒正在等待的底層IO資源,如關閉Socket
還有個更好的選擇:nio,提供了更人性化的中斷,被阻塞的nio通道會自動響應中斷

4 例項

無法被打斷的例子

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(new Runnable() {

        @Override
        public void run() {
            while(true){
                System.out.println("go on...");
            }
        }
    });

    t.start();

    Thread.sleep(2000);
    t.interrupt();

    while(true){
        Thread.yield();
    }
}

如果一個Runnable沒有cancel類的標誌位檢查,也沒有檢查isInterrupt(),呼叫interrupt()會怎麼地?  
是關不掉的,但這種形式的無限迴圈,一般不會出現在真實場景裡
真實場景什麼樣?一般是一個無限迴圈,但裡面會阻塞(等待條件成熟),有跳出條件  

處理InterrupttedException

執行緒被中斷之後,會發異常InterrupttedException,有時需要清理資源,參考類c10.InterruptingIdiom


class NeedsCleanup {
  private final int id;
  public NeedsCleanup(int ident) {
    id = ident;
    System.out.println("NeedsCleanup " + id);
  }
  public void cleanup() {
    System.out.println("Cleaning up " + id);
  }
}

class Blocked3 implements Runnable {
  private volatile double d = 0.0;
  public void run() {
    try {
      while(!Thread.interrupted()) {
        // point1
        NeedsCleanup n1 = new NeedsCleanup(1);
        // Start try-finally immediately after definition
        // of n1, to guarantee proper cleanup of n1:
        try {
          System.out.println("Sleeping");
          TimeUnit.SECONDS.sleep(1);
          // point2
          NeedsCleanup n2 = new NeedsCleanup(2);
          // Guarantee proper cleanup of n2:
          try {
            System.out.println("Calculating");
            // A time-consuming, non-blocking operation:
            for(int i = 1; i < 2500000; i++)
              d = d + (Math.PI + Math.E) / d;
            System.out.println("Finished time-consuming operation");
          } finally {
            n2.cleanup();
          }
        } finally {
          n1.cleanup();
        }
      }
      System.out.println("Exiting via while() test");
    } catch(InterruptedException e) {
      System.out.println("Exiting via InterruptedException");
    }
  }
}

public class InterruptingIdiom {
  public static void main(String[] args) throws Exception {
    long delay = 2000;
    Thread t = new Thread(new Blocked3());
    t.start();
    TimeUnit.MILLISECONDS.sleep(delay);
    t.interrupt();
  }
} /* Output: (Sample)
NeedsCleanup 1
Sleeping
NeedsCleanup 2
Calculating
Finished time-consuming operation
Cleaning up 2
Cleaning up 1
NeedsCleanup 1
Sleeping
Cleaning up 1
Exiting via InterruptedException
*///:~

相關推薦

Java 6-2自如融匯貫通執行不再——執行阻塞關閉

本節重點說說執行緒什麼時候會阻塞,如何關閉 1 讓出時間片 Thread.yield(); 通知並建議執行緒排程器,我已經做完了主要工作,時間片你可以分給別人了 即使呼叫了這個,還是可能沒有切換時間片,或者切換了,但是還是給了當前執行緒 Threa

遲到的年度總結我們應該自如

現在 別人 公眾號 身體 地產 鍵盤 gpo 失去 不想 現在已經2018年3月了,這篇2017的年度總結顯得晚了一點,但意義非凡,讓我有了更多的反省和思考,我相信大家讀完一定會有收獲。好了,廢話不多說進入主題。 別用學習來逃避成長 這話說的簡直是屁話,不是學習才會成長嗎

4S店使用OA系統實現集團化管控權力自如

4S店集團化管理瓶頸集中表現為: 組織龐大:區域分公司+銷售公司+店面,集中部署工作難,管控更難; 業務多樣:汽車與配件銷售、售後、保險等,統一標準、規範化執行難; 系統複雜:內部管理擁有人事、財務、業務等多套系統,維護成本極高。 泛微OA系統整合眾

Java 6-4別看了坑的就是你——活性失敗-volatile

執行緒A對某變數值的修改,可能沒有立即線上程B體現出來,稱為活性失敗。 注意下面這個例子,在PC和安卓上執行結果可能不一樣(安卓可能不需要volatile) 例子 public class VolatileTest extends Thread {

java基礎增強統計網上app下載情況並排序

技術 rri map對象 cat -s height hang city ole 一入編程深似海,從此妹子是路人。 案例:   統計網站app下載的情況,後臺數據如下:     日期,用戶名,app名,下載渠道,所在城市,app版本     2017-08-15,xx老師,

javacpp-FFmpeg系列之2通用拉流解碼器支持視頻拉流解碼並轉換為YUV、BGR24或RGB24等圖像像素數據

tope sca 封裝 ams 定義 throw tco 如何使用 都是 javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:視頻拉流解碼成YUVJ420P,並保存為jpg圖片 javacpp-FFmpeg系列之2:通用拉流解碼器,支持視頻拉流解碼並

資料結構實現 6.2優先佇列_基於最大二叉堆實現(C++版)

資料結構實現 6.2:優先佇列_基於最大二叉堆實現(C++版) 1. 概念及基本框架 2. 基本操作程式實現 2.1 入隊操作 2.2 出隊操作 2.3 查詢操作 2.4 其他操作 3. 演算法複雜度分析

程式設計之美2程式只用一個位元組變數列印將帥位置

原創:https://blog.csdn.net/ndzjx/article/details/84404320 #include <stdio.h> #include <windows.h> #include <time.h> #include <

JAVA自查2方法理解

方法理解 修飾符 返回型別 方法名稱([引數型別 變數, ......]) { 方法體程式碼; [return [返回值];] } 其中返回值型別可以是基本資料型別,也可以是引用型別,還可以是空(void) 基本資料型別有八個:byte(1),short(2),in

[瘋狂Java]NIO.2WatchService、WatchKey(監控檔案變化)

1. 舊版本監控檔案變化的弊端:     1) 非常繁瑣,必須自己手動開啟一個後臺執行緒每隔一段時間遍歷一次目標節點並記錄當前狀態,然後和上一次遍歷的狀態對比,如果不相同就表示發生了變化,再採取相應的操作,這個過程非常長,都需要使用者自己手動實現;     2) 效率低:

Java小白到獲BAT等offer分享我這兩年的經驗感悟

也不能 協調 tag 自信 旅行 java核心技術 虛擬機 ren tps 點擊關註並置頂,江湖要事早提醒 點擊關註並指定,江湖要事早知道 我 常想,人生最有趣莫過於前路未知。於是我常常羨慕那些個“金梁古溫”筆下隨遇而安、隨性而為、隨緣而愛的浪子們。

Java併發程式設計volatile關鍵字解析-原子性可見性有序性

volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。   volatile關鍵字雖然從字面上理解起來比較簡單,但是要用好不是一件容易的事情

YOLOV3實戰2訓練自己的資料集你不可能出錯!

大家好,我是小p,今天給大家帶來一期用darknet版本YOLO V3訓練自己資料集的教程,希望大家喜歡。 歡迎加入物件檢測群813221712討論和交流,進群請看群公告! 一、搭建環境 搭建環境和驗證環境是否已經正確配置已在YOLOV3實戰1中詳細介紹,請一定

win2012R2安裝net4.6.2失敗提示“更新2919355包問題或者win8.1、win10”的錯誤

ron msu download target inf compress .aspx 情況下 rdquo 前言 在客戶的服務器電腦安裝net4.6,提示安裝失敗錯誤,最後順利成功安裝net4.6。 一、錯誤 1、win2012R2安裝net4.6.2失敗提示&ldqu

Java Maven專案spring boot + Mybatis連線MySQL通用mapper的增刪改查對映實現多表查詢

1. MySQL自帶庫test新增表user、role 角色表role 使用者表user 2. 新增依賴,配置屬性 相關依賴:百度即可,此處略 application.properties spring.application.name=clean-exe

Java基礎(2)Java面向物件

Java面向物件 1 Java類與物件 1.1 類 1.2 物件 1.3 內部類 2 面向物件三大特徵 2.1 封裝 2.2 繼承 2.3 多型 3 抽象類 4 介面

Java併發2JMM,volatile,synchronized,final

併發程式設計的兩個關鍵問題 併發程式設計需要處理兩個關鍵問題:執行緒之間如何通訊以及執行緒之間如何同步。 通訊是指執行緒之間以何種機制來交換資訊。執行緒之間的通訊機制有兩種:共享記憶體和訊息傳遞。 共享記憶體模型中,執行緒之間共享程式的公共狀態,通過讀-寫記憶體中的公共狀態進行隱式通訊。多條執行緒共享一

【新手實戰】redhat 6.2 升級openSSH (配合安裝GCC升級openSSL)

致謝:CSDN;linux公社-編輯Linux、wyb7821、wangyan以及未列出的網際網路前輩。 背景:公司的SFTP實體機(RedHat 6.2 x86_64)被綠盟掃描出openSSH工具有漏洞,需要進行升級。登入到伺服器一看悲劇了,沒有GCC編譯器!原來5年前

java面試(2)int、char、long各佔多少位元組數

java的基本型別各佔位元組數: boolean 未指定,至少一個位元組。僅定義取字面值true和false。 byte 1位元組 short & char

Java經典例項進階版堆疊實現支援任何物件型別

支援任何物件型別,有更多的錯誤檢查。 package Stack; /** * Created by Frank */ public class MyStack<T> implements SimpleStack<T> { public stati