2020119-多執行緒暫停和喚醒
- 最近在工作中有涉及到工作流的暫停和喚醒的設計和開發任務,裡面有些坑,拿出來分享下
- 工作流的暫停和喚醒主要有三種方式:
- 第一種使用wait(), notify():
-
/** * 執行緒阻塞/喚醒 * 2020/11/19 * chenzhen */ public class MyThread extends Thread { private boolean isWait =false; // private final Object lock = new Object(); //標誌執行緒阻塞情況 private boolean pause = false; //設定執行緒是否阻塞 public void pauseThread() { this.pause = true; } //呼叫該方法實現恢復執行緒的執行 public void resumeThread() { this.pause = false; synchronized (lock) { //喚醒執行緒 lock.notify(); } } //這個方法只能在run方法中實現,不然會阻塞主執行緒,導致頁面無響應 void onPause() { synchronized (lock) { try { //執行緒等待/阻塞 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void run() { super.run(); //標誌執行緒開啟 isWait = true; //一直迴圈 while (true) { if (pause) { //執行緒阻塞/等待 onPause(); } try { //程式每50毫秒執行一次, 值可更改 Thread.sleep(50); //業務邏輯 } catch (InterruptedException e) { e.printStackTrace(); break; } } } public static void main(String[] args) { //1,建立自己的執行緒 MyThread myThread = new MyThread(); //2,在合適的地方啟動執行緒 myThread.start(); //3,啟動後執行緒的阻塞/暫停 myThread.pauseThread(); //4,阻塞/暫停執行緒後喚醒/繼續 myThread.resumeThread(); } }
4, 第二種使用 await() 和signal()
-
/** * 執行緒阻塞/喚醒 * 2020/11/19 * chenzhen */ public class Test { public static Lock lock = new ReentrantLock(); public static int count = 0; public static Condition conditionA = lock.newCondition(); public static Condition conditionB = lock.newCondition(); public static void main(String[] args) { Thread t1 = new Thread() { @Override public void run() { lock.lock(); if (count < 5) { System.out.println("執行緒1未達到業務要求,暫停中,等待執行緒2處理到達到要求後喚醒"); try { conditionA.await();// 暫停執行緒並釋放鎖 System.out.println("conditionA被喚醒"); conditionB.await(); System.out.println("conditionB被喚醒"); System.out.println("我是執行緒1後面的程式碼"); } catch (InterruptedException e) { e.printStackTrace(); } } lock.unlock(); } }; Thread t2 = new Thread() { @Override public void run() { lock.lock(); while (count < 10) { count++; System.out.println("執行緒2業務處理中: " + count); try { Thread.sleep(1000); if (count == 5) { conditionA.signal(); System.out.println("喚醒執行緒1"); lock.unlock();// 呼叫signal()方法後,執行緒2並不會釋放鎖,需要手動釋放執行緒2才會執行 } } catch (InterruptedException e) { e.printStackTrace(); } } try { lock.lock();// 不加這個會報java.lang.IllegalMonitorStateException System.out.println("等待3秒後conditionB會被喚醒"); Thread.sleep(3000); conditionB.signal(); } catch (InterruptedException e) { e.printStackTrace(); } lock.unlock();// 這裡釋放鎖,執行緒2執行完,執行緒1才會執行 } }; t1.start(); t2.start(); } }
5, 第三種使用Jdk6中的LockSupport
-
/** * 執行緒阻塞/喚醒-使用LockSupport 實現指定執行緒的暫停和喚醒 * 2020/11/19 * chenzhen */ public class TestLockSupport { public static void main(String[] args) throws Exception { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("start..."); LockSupport.park(); //一直wait System.out.println("continue..."); } }); thread.start(); Thread.sleep(10000); LockSupport.unpark(thread); //指定t執行緒解除wait狀態 } }
6, 專案中的需求是: 系統中有很多工作流在執行,可以手動實現指定工作流暫停和喚醒,我採用的是第三種,
park和unpark
可以實現類似wait和notify
的功能,但是並不和wait和notify
交叉,也就是說unpark
不會對wait
起作用,notify
也不會對park
起作用。park和unpark
的使用不會出現死鎖的情況- blocker的作用是在dump執行緒的時候看到阻塞物件的資訊
通過檢視原始碼可以看到park只能暫停當前執行緒,不能暫停指定執行緒, 所以我採用重寫LockSupport方式
/**
* 執行緒阻塞/喚醒 -自定義LockSupport
* 2020/11/19
* chenzhen
*/
public class LockSupportImpl{
private LockSupportImpl() {} // Cannot be instantiated.
/**
* 暫停指定執行緒
* @param blocker
* @param t
*/
public static void park(Object blocker,Thread t) {
//Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
public static void park() {
UNSAFE.park(false, 0L);
}
// Hotspot implementation via intrinsics API
private static final sun.misc.Unsafe UNSAFE;
private static final long parkBlockerOffset;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
}
/**
* 執行緒阻塞/喚醒
* 2020/11/19
* chenzhen
*/
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Thread thread = new Thread(
() -> {
//業務邏輯省略
}
);
Future<?> submit = executorService.submit(thread);
LockSupportImpl.park("執行緒:"+thread.getName(),thread);
LockSupportImpl.unpark(thread);
}
}
這裡實現了
相關文章
- 多執行緒——虛假喚醒執行緒
- Java 多執行緒基礎(六)執行緒等待與喚醒Java執行緒
- Java多執行緒推薦使用的停止方法和暫停方法Java執行緒
- 工作執行緒的喚醒及建立(19)執行緒
- 執行緒虛假喚醒問題剖析執行緒
- 從 JDK 原始碼角度看執行緒的阻塞和喚醒JDK原始碼執行緒
- 多執行緒-生產者消費者之等待喚醒機制執行緒
- ManualResetEvent實現執行緒的暫停與恢復執行緒
- 執行緒等待兩種方法的喚醒的效率比較執行緒
- java多執行緒之消費生產模型-使用synchronized解決虛假喚醒Java執行緒模型synchronized
- 多執行緒和多執行緒同步執行緒
- Java通過join方法來暫停當前執行緒Java執行緒
- 多執行緒-生產者消費者之等待喚醒機制程式碼優化執行緒優化
- 破除java神話之執行緒按優先順序喚醒Java執行緒
- Linux中執行緒的掛起與恢復(程式暫停)Linux執行緒
- 多執行緒-程式和執行緒的概述執行緒
- dubbo傳送過程編碼失敗,會喚醒傳送執行緒嗎?執行緒
- 多執行緒-執行緒組的概述和使用執行緒
- 多執行緒-執行緒池的概述和使用執行緒
- .NET多執行緒程式設計(1):多工和多執行緒 (轉)執行緒程式設計
- Java 併發程式設計學習筆記 05 :如何暫停執行緒?Java程式設計筆記執行緒
- 多執行緒:執行緒池理解和使用總結執行緒
- 瀏覽器多執行緒和js單執行緒瀏覽器執行緒JS
- 多執行緒【執行緒池】執行緒
- 多執行緒--執行緒管理執行緒
- Java多執行緒——執行緒Java執行緒
- 執行緒與多執行緒執行緒
- 多執行緒:Operation和OperationQueue執行緒
- VC多執行緒 C++ 多執行緒執行緒C++
- ObjC 多執行緒簡析(一)-多執行緒簡述和執行緒鎖的基本應用OBJ執行緒
- 多執行緒-執行緒控制之休眠執行緒執行緒
- 多執行緒-執行緒控制之加入執行緒執行緒
- 多執行緒-執行緒控制之禮讓執行緒執行緒
- 多執行緒-執行緒控制之中斷執行緒執行緒
- win32多執行緒和C++11多執行緒對比Win32執行緒C++
- JAVA多執行緒詳解(3)執行緒同步和鎖Java執行緒
- 多執行緒設計模式:保護性暫停模式詳解以及其在DUBBO應用原始碼分析執行緒設計模式原始碼
- Java併發包原始碼學習系列:掛起與喚醒執行緒LockSupport工具類Java原始碼執行緒