<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
執行緒是主要負責執行指令,程序是主要管載入指令。
一個程序之內可以分為一到多個執行緒。
一個執行緒就是一個指令流,將指令流中的一條條指令以一定的順序交給 CPU 執行
Java 中,執行緒作為最小排程單位,程序作為資源分配的最小單位。 在 windows 中程序是不活動的,只是作為執行緒的容器
使用多核cpu並行執行可以明顯的提高執行效率
注意:單核依然是並行的思想(即:cpu輪流去執行執行緒,微觀上仍舊是序列),使用單核的多執行緒可能會比使用單核的單執行緒慢,這是因為多執行緒上下文切換反而浪費了時間。
1.使用 Thread
public static void main(String[] args) { // 建立執行緒物件 Thread t = new Thread("執行緒1") { public void run() { // 要執行的任務 log.debug("執行緒1被啟動了"); } }; // 啟動執行緒 t.start(); log.debug("測試"); }
2.使用 Runnable 配合 Thread
public static void main(String[] args) { Runnable runnable = new Runnable() { public void run() { // 要執行的任務 log.debug("執行緒1被啟動了"); } }; // 建立執行緒物件 Thread t = new Thread(runnable); t.setName("執行緒1"); // 啟動執行緒 t.start(); log.debug("測試"); }
這裡的Runnable是一個介面,介面中只有一個抽象方法,由我們來提供實現,實現中包含執行緒的程式碼就可以了。
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
方法1原理分析
方法2是使用runnable物件,當成引數傳給Thread構造方法,其中又呼叫了init方法,下面是Thread構造方法的原始碼
public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }
繼續跟蹤檢視runnable物件傳到哪裡去了,可以看到又傳給了另一個過載的init,如下
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); }private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); }
再次跟蹤可以看到是把runnable物件傳給了一個thread的一個成員變數
//省略部分程式碼 this.target = target;
那麼這個成員變數在哪裡在使用了呢,經過查詢可以發現是在run方法裡面,只不過Thread發現有runnable物件就會先採用runnable的run方法。
@Override public void run() { if (target != null) { target.run(); } }
方法2原理分析
通過建立一個子類去重寫Thread類的run方法,這樣就不會執行父類別的run方法。
1.用 Runnable 更容易與執行緒池等高階 API 配合
2.用 Runnable 讓任務類脫離了 Thread 繼承體系,更靈活
方法3 FutureTask配合Thread建立執行緒
Future介面中含有get方法來返回結果的
//省略部分程式碼 V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
而runnable本身是沒有返回結果的,runnable不能將結果傳給其他執行緒。
public interface Runnable { public abstract void run(); }
要注意到FutureTask也實現了Runnable介面,也可以傳給Thread的有參構造裡面。
建立執行緒的程式碼
public static void main(String[] args) throws ExecutionException, InterruptedException { // 建立任務物件 FutureTask<Integer> task3 = new FutureTask<>(new Callable<Integer>() { @Override public Integer call() throws Exception { log.debug("執行緒1被執行了"); return 666; } }); // 引數1 是任務物件; 引數2 是執行緒名字,推薦 new Thread(task3, "執行緒1").start(); // 主執行緒阻塞,同步等待 task 執行完畢的結果 Integer result = task3.get(); log.debug("結果是:{}", result); }
tasklist
檢視程序taskkill
殺死程序JVM 中由堆、棧、方法區所組成,其中棧就是給執行緒使用的。
方法呼叫時,就會對該方法產生一個棧幀,方法的區域性變數都會在棧幀中儲存。棧是後進先出,當method2執行完就會回收,在執行完同時會記錄返回地址,然後在method1中繼續執行。
執行緒之間的棧幀是相互獨立的,之間互不干擾。
當上下文切換時,要儲存當前的狀態,因為可能是時間片用完了,此時執行緒還沒有結束。Java中對應的就是程式計數器
啟動一個執行緒必須要用start方法,如果直接呼叫類裡面的run方法實際走的是main主執行緒。
執行緒start前getState()
得到的是NEW
執行緒start後getState()
得到的是RUNNABLE
public static void main(String[] args) { Thread t1 = new Thread("t1") { @Override public void run() { log.debug("t1被啟動"); } }; System.out.println(t1.getState()); t1.start(); System.out.println(t1.getState()); }
在sleep期間呼叫getState()
方法可以得到TIMED_WAITING
public static void main(String[] args) { Thread t1 = new Thread("執行緒1") { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }; t1.start(); log.debug("執行緒1 state: {}", t1.getState()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("執行緒1 state: {}", t1.getState()); }
sleep可以使用interrupt
方法打斷,打斷後會觸發InterruptedException
異常
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread("t1") { @Override public void run() { log.debug("進入睡眠"); try { Thread.sleep(2000); } catch (InterruptedException e) { log.debug("被喚醒"); e.printStackTrace(); } } }; t1.start(); Thread.sleep(1000); log.debug("打斷"); t1.interrupt(); }
sleep防止cpu使用100%
在沒有利用cpu來計算時,不要讓while(true)空轉浪費cpu,這時可以使用yield或 sleep 來讓出cpu的使用權給其他程式
yield方法會把cpu的使用權讓出去,然後排程執行其它執行緒。執行緒的排程最終還是依賴的作業系統的排程器。
該方法會等待執行緒的結束
static int r = 11; public static void main(String[] args) throws InterruptedException { test1(); } private static void test1() throws InterruptedException { log.debug("主執行緒開始"); Thread t1 = new Thread(() -> { sleep(1); r = 888; },"執行緒1"); t1.start(); // t1.join(); log.debug(String.valueOf(r)); log.debug("主執行緒執行緒結束"); }
join沒有使用時,返回的是11,若是使用join返回的是888,是主執行緒在同步等待執行緒1。
當然還有其他的方法等待
1.sleep方法等待執行緒1結束
2.利用FutureTask的get方法
join(long n)方法可以傳入引數,等待執行緒執行結束,最多等待 n 毫秒,假如執行時間大於等待的時間,就會不再等待。那麼該執行緒會直接結束嗎?答案是不會。
如下程式碼
public class Test10 { static int r = 11; public static void main(String[] args) throws InterruptedException { test1(); } private static void test1() throws InterruptedException { log.debug("主執行緒開始"); Thread t1 = new Thread(() -> { sleep(2); r = 888; log.debug("執行緒1結束"); },"執行緒1"); t1.start(); t1.join(1000); log.debug(String.valueOf(r)); log.debug("主執行緒執行緒結束"); } }
輸出結果,可以看到這裡只是主執行緒不再等待。
16:28:53.360 c.Test10 [main] - 主執行緒開始
16:28:54.411 c.Test10 [main] - 11
16:28:54.411 c.Test10 [main] - 主執行緒執行緒結束
16:28:55.404 c.Test10 [執行緒1] - 執行緒1結束
interrupt可以用來打斷處於阻塞狀態的執行緒。在打斷後,會有一個打斷標記(布林值)會提示是否被打斷過,被打斷過標記為true
否則為false
.
但是sleep、wait和join可以來清空打斷標記。
程式碼如下
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { log.debug("sleep..."); try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } },"t1"); t1.start(); Thread.sleep(1000); log.debug("interrupt"); t1.interrupt(); log.debug("打斷標記:{}", t1.isInterrupted()); }
執行緒被打斷後並不會結束執行,有人就會問了,那我們如何在打斷執行緒後關閉執行緒呢?答案就是利用打斷標記去實現。
可以線上程的死迴圈之中加入一個判斷去實現。
boolean interrupted = Thread.currentThread().isInterrupted(); if(interrupted) { log.debug("退出迴圈"); break; }
Java 程序通常需要所有執行緒都執行結束,才會結束。
但是存在一種守護行程,只要其他非守護行程結束,守護行程就會結束。垃圾回收器就使用的守護行程。
作業系統層面(早期程序的狀態)
1、新建狀態(New)
Thread t1 = new Thread("t1") { @Override public void run() { log.debug("running..."); } }; log.debug("t1 state {}", t1.getState());
2、就緒狀態(Runnable)與執行狀態(Running)
Thread t2 = new Thread("t2") { @Override public void run() { while(true) { // runnable } } }; t2.start(); log.debug("t2 state {}", t2.getState());
3、阻塞狀態(Blocked)
用一個執行緒拿到鎖,使得當前執行緒沒拿到鎖會出現阻塞狀態。
Thread t6 = new Thread("t6") { @Override public void run() { synchronized (TestState.class) { // blocked try { Thread.sleep(90000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; t6.start();
4、等待狀態(Waiting)
等待一個未執行完成的執行緒
t2.join(); //等待狀態
5、超時等待(Time_Waiting)
可以在指定的時間自行返回的。
6、終止狀態(TERMINATED)
執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。 終止的執行緒不可再次復生。
本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45