摘要:是不能直接調(diào)用系統(tǒng)功能的,所以,我們沒有辦法直接實(shí)現(xiàn)多線程程序。通過查看,我們知道了有種方式實(shí)現(xiàn)多線程程序。使用的是搶占式調(diào)度模型演示如何設(shè)置和獲取線程優(yōu)先級返回線程對象的優(yōu)先級更改線程的優(yōu)先級線程默認(rèn)優(yōu)先級是。線程優(yōu)先級的范圍是。
第五階段 多線程 前言:
一個(gè)場景:周末,帶著并不存在的女票去看電影,無論是現(xiàn)場買票也好,又或是手機(jī)買票也好,上一秒還有位置,遲鈍了一下以后,就顯示該座位已經(jīng)無法選中,一不留神就沒有座位了,影院的票是一定的,但是究竟是如何做到,多個(gè)窗口或者用戶同時(shí)出票而又不重復(fù)的呢? 這就是我們今天所要講解的多線程問題(一) 線程和進(jìn)程的概述 (1) 進(jìn)程
進(jìn)程:進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)用的獨(dú)立單位。每一個(gè)進(jìn)程都有它自己的內(nèi)存空間和系統(tǒng)資源
多線程:在同一個(gè)時(shí)間段內(nèi)可以執(zhí)行多個(gè)任務(wù),提高了CPU的使用率
(2) 線程線程:進(jìn)程的執(zhí)行單元,執(zhí)行路徑
單線程:一個(gè)應(yīng)用程序只有一條執(zhí)行路徑
多線程:一個(gè)應(yīng)用程序有多條執(zhí)行路徑
多進(jìn)程的意義?—— 提高CPU的使用率
多線程的意義? —— 提高應(yīng)用程序的使用率
(3) 補(bǔ)充并行和并發(fā)
并行是物理上同時(shí)發(fā)生,指在某一個(gè)時(shí)間點(diǎn)同時(shí)運(yùn)行多個(gè)程序
并發(fā)是邏輯上同時(shí)發(fā)生,指在某一個(gè)時(shí)間段內(nèi)同時(shí)運(yùn)行多個(gè)程序
Java程序運(yùn)行原理和JVM的啟動(dòng)是否是多線程的 ?
Java程序的運(yùn)行原理:
由java命令啟動(dòng)JVM,JVM啟動(dòng)就相當(dāng)于啟動(dòng)了一個(gè)進(jìn)程
接著有該進(jìn)程創(chuàng)建了一個(gè)主線程去調(diào)用main方法
JVM虛擬機(jī)的啟動(dòng)是單線程的還是多線程的 ?
垃圾回收線程也要先啟動(dòng),否則很容易會出現(xiàn)內(nèi)存溢出
現(xiàn)在的垃圾回收線程加上前面的主線程,最低啟動(dòng)了兩個(gè)線程,所以,jvm的啟動(dòng)其實(shí)是多線程的
JVM啟動(dòng)至少啟動(dòng)了垃圾回收線程和主線程,所以是多線程的
(二) 多線程代碼實(shí)現(xiàn)需求:我們要實(shí)現(xiàn)多線程的程序。
如何實(shí)現(xiàn)呢?
由于線程是依賴進(jìn)程而存在的,所以我們應(yīng)該先創(chuàng)建一個(gè)進(jìn)程出來。
而進(jìn)程是由系統(tǒng)創(chuàng)建的,所以我們應(yīng)該去調(diào)用系統(tǒng)功能創(chuàng)建一個(gè)進(jìn)程。
Java是不能直接調(diào)用系統(tǒng)功能的,所以,我們沒有辦法直接實(shí)現(xiàn)多線程程序。
但是呢?Java可以去調(diào)用C/C++寫好的程序來實(shí)現(xiàn)多線程程序。
由C/C++去調(diào)用系統(tǒng)功能創(chuàng)建進(jìn)程,然后由Java去調(diào)用這樣的東西,
然后提供一些類供我們使用。我們就可以實(shí)現(xiàn)多線程程序了。
通過查看API,我們知道了有2種方式實(shí)現(xiàn)多線程程序。
方式1:繼承Thread類步驟:
自定義MyThread(自定義類名)繼承Thread類
MyThread類中重寫run()
創(chuàng)建對象
啟動(dòng)線程
public class MyThread extends Thread{ public MyThread() { } @Override public void run() { for (int i = 0; i < 100; i++){ System.out.println(getName() + ":" + i); } } }
public class MyThreadTest { public static void main(String[] args) { //創(chuàng)建線程對象 MyThread my = new MyThread(); //啟動(dòng)線程,run()相當(dāng)于普通方法的調(diào)用,單線程效果 //my.run(); //首先啟動(dòng)了線程,然后再由jvm調(diào)用該線程的run()方法,多線程效果 my.start(); //兩個(gè)線程演示,多線程效果需要?jiǎng)?chuàng)建多個(gè)對象而不是一個(gè)對象多次調(diào)用start()方法 MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); my1.start(); my2.start(); } } //運(yùn)行結(jié)果 Thread-1:0 Thread-1:1 Thread-1:2 Thread-0:0 Thread-1:3 Thread-0:1 Thread-0:2 ...... Thread-0:95 Thread-0:96 Thread-0:97 Thread-0:98 Thread-0:99方式2:實(shí)現(xiàn)Runnable接口 (推薦)
步驟:
自定義類MyuRunnable實(shí)現(xiàn)Runnable接口
重寫run()方法
創(chuàng)建MyRunable類的對象
創(chuàng)建Thread類的對象,并把C步驟的對象作為構(gòu)造參數(shù)傳遞
public class MyRunnable implements Runnable { public MyRunnable() { } @Override public void run() { for (int i = 0; i < 100; i++){ //由于實(shí)現(xiàn)接口的方式不能直接使用Thread類的方法了,但是可以間接的使用 System.out.println(Thread.currentThread().getName() + ":" + i); } } }
public class MyRunnableTest { public static void main(String[] args) { //創(chuàng)建MyRunnable類的對象 MyRunnable my = new MyRunnable(); //創(chuàng)建Thread類的對象,并把C步驟的對象作為構(gòu)造參數(shù)傳遞 // Thread t1 = new Thread(my); // Thread t2 = new Thread(my); //下面具體講解如何設(shè)置線程對象名稱 // t1.setName("User1"); // t1.setName("User2"); Thread t1 = new Thread(my,"User1"); Thread t2 = new Thread(my,"User2"); t1.start() t2.start(); } }
實(shí)現(xiàn)接口方式的好處
可以避免由于Java單繼承帶來的局限性
適合多個(gè)相同程序的代碼去處理同一個(gè)資源的情況,把線程同程序的代碼,數(shù)據(jù)有效分離,較好的體現(xiàn)了面向?qū)ο蟮脑O(shè)計(jì)思想
如何理解------可以避免由于Java單繼承帶來的局限性
比如說,某個(gè)類已經(jīng)有父類了,而這個(gè)類想實(shí)現(xiàn)多線程,但是這個(gè)時(shí)候它已經(jīng)不能直接繼承Thread類了
(接口可以多實(shí)現(xiàn)implements,但是繼承extends只能單繼承) ,它的父類也不想繼承Thread因?yàn)椴恍枰獙?shí)現(xiàn)多線程
(三) 獲取和設(shè)置線程對象//獲取線程的名稱 public?final?String?getName() //設(shè)置線程的名稱 public?final?void?setName(String?name)設(shè)置線程的名稱 (如果不設(shè)置名稱的話,默認(rèn)是Thread-??(編號) )
方法一:無參構(gòu)造 + setXxx (推薦)
//創(chuàng)建MyRunnable類的對象 MyRunnable my = new MyRunnable(); //創(chuàng)建Thread類的對象,并把C步驟的對象作為構(gòu)造參數(shù)傳遞 Thread t1 = new Thread(my); Thread t2 = new Thread(my); t1.setName("User1"); t1.setName("User2"); //與上面代碼等價(jià) Thread t1 = new Thread(my,"User1"); Thread t2 = new Thread(my,"User2");
方法二:(稍微麻煩,要手動(dòng)寫MyThread的帶參構(gòu)造方法,方法一不用)
//MyThread類中 public MyThread(String name){ super(name);//直接調(diào)用父類的就好 } //MyThreadTest類中 MyThread my = new MyThread("admin");獲取線程名稱
注意:重寫run方法內(nèi)獲取線程名稱的方式
//Thread getName() //Runnable //由于實(shí)現(xiàn)接口的方式不能直接使用Thread類的方法了,但是可以間接的使用 Thread.currentThread().getName()
使用實(shí)現(xiàn)Runnable接口方法的時(shí)候注意:main方法所在的測試類并不繼承Thread類,因此并不能直接使用getName()方法來獲取名稱。
//這種情況Thread類提供了一個(gè)方法: //public static Thread currentThread(): //返回當(dāng)前正在執(zhí)行的線程對象,返回值是Thread,而Thread恰巧可以調(diào)用getName()方法 System.out.println(Thread.currentThread().getName());(四) 線程調(diào)度及獲取和設(shè)置線程優(yōu)先級
假如我們的計(jì)算機(jī)只有一個(gè) CPU,那么 CPU 在某一個(gè)時(shí)刻只能執(zhí)行一條指令,線程只有得到 CPU時(shí)間片,也就是使用權(quán),才可以執(zhí)行指令。那么Java是如何對線程進(jìn)行調(diào)用的呢?線程有兩種調(diào)度模型:
分時(shí)調(diào)度模型 :所有線程輪流使用 CPU 的使用權(quán),平均分配每個(gè)線程占用 CPU 的時(shí)間片
搶占式調(diào)度模型 :優(yōu)先讓優(yōu)先級高的線程使用 CPU,如果線程的優(yōu)先級相同,那么會隨機(jī)選擇一個(gè),優(yōu)先級高的線程獲取的 CPU 時(shí)間片相對多一些。
Java使用的是搶占式調(diào)度模型
//演示如何設(shè)置和獲取線程優(yōu)先級 //返回線程對象的優(yōu)先級 public final int getPriority() //更改線程的優(yōu)先級 public final void setPriority(int newPriority)
線程默認(rèn)優(yōu)先級是5。
線程優(yōu)先級的范圍是:1-10。
線程優(yōu)先級高僅僅表示線程獲取的 CPU時(shí)間片的幾率高,但是要在次數(shù)比較多,或者多次運(yùn)行的時(shí)候才能看到比較好的效果。
(五) 線程控制在后面的案例中會用到一些,這些控制功能不是很難,可以自行測試。
//線程休眠 public static void sleep(long millis) //線程加入(等待該線程終止,主線程結(jié)束后,其余線程開始搶占資源) public final void join() //線程禮讓(暫停當(dāng)前正在執(zhí)行的線程對象,并且執(zhí)行其他線程讓多個(gè)線程的執(zhí)行更加和諧,但是不能保證一人一次) public static void yield() //后臺線程(某線程結(jié)束后,其他線程也結(jié)束) public final void setDaemon(boolean on) //(過時(shí)了但還可以用) public final void stop() //中斷線程 public void interrupt()(六) 線程的生命周期
新建 —— 創(chuàng)建線程對象
就緒 —— 線程對象已經(jīng)啟動(dòng),但是還沒有獲取到CPU的執(zhí)行權(quán)
運(yùn)行 —— 獲取到了CPU的執(zhí)行權(quán)
阻塞 —— 沒有CPU的執(zhí)權(quán),回到就緒
死亡 —— 代碼運(yùn)行完畢,線程消亡
(七) 多線程電影院出票案例public class SellTickets implements Runnable { private int tickets = 100; @Override public void run() { while (true){ if (tickets > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"); } } } }
public class SellTicketsTest { public static void main(String[] args) { //創(chuàng)建資源對象 SellTickets st = new SellTickets(); //創(chuàng)建線程對象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); //啟動(dòng)線程 t1.start(); t2.start(); t3.start(); } }
在SellTicket類中添加sleep方法,延遲一下線程,拖慢一下執(zhí)行的速度
通過加入延遲后,就產(chǎn)生了連個(gè)問題:
A:相同的票賣了多次
CPU的一次操作必須是原子性(最簡單的)的 (在讀取tickets--的原來的數(shù)值和減1之后的中間擠進(jìn)了兩個(gè)線程而出現(xiàn)重復(fù))
B:出現(xiàn)了負(fù)數(shù)票
隨機(jī)性和延遲導(dǎo)致的 (三個(gè)線程同時(shí)擠進(jìn)一個(gè)循環(huán)里,tickets--的減法操作有可能在同一個(gè)循環(huán)中被執(zhí)行了多次而出現(xiàn)越界的情況,比如說 tickets要大于0卻越界到了-1)
也就是說,線程1執(zhí)行的同時(shí)線程2也可能在執(zhí)行,而不是線程1執(zhí)行的時(shí)候線程2不能執(zhí)行。
我們先要知道一下哪些問題會導(dǎo)致出問題:
而且這些原因也是以后我們判斷一個(gè)程序是否會有線程安全問題的標(biāo)準(zhǔn)
A:是否是多線程環(huán)境
B:是否有共享數(shù)據(jù)
C:是否有多條語句操作共享數(shù)據(jù)
我們對照起來,我們的程序確實(shí)存在上面的問題,因?yàn)樗鼭M足上面的條件
那我們怎么來解決這個(gè)問題呢?
把多條語句操作共享數(shù)據(jù)的代碼給包成一個(gè)整體,讓某個(gè)線程在執(zhí)行的時(shí)候,別人不能來執(zhí)行
Java給我們提供了:同步機(jī)制
//同步代碼塊: synchronized(對象){ 需要同步的代碼; }
同步的好處
同步的出現(xiàn)解決了多線程的安全問題
同步的弊端
當(dāng)線程相當(dāng)多時(shí),因?yàn)槊總€(gè)線程都會去判斷同步上的鎖,這是很耗費(fèi)資源的,無形中會降低程序的運(yùn)行效率
概述:
A:同步代碼塊的鎖對象是誰呢?
任意對象
B:同步方法的格式及鎖對象問題?
把同步關(guān)鍵字加在方法上
同步方法的鎖對象是誰呢?
this
C:靜態(tài)方法及鎖對象問題?
靜態(tài)方法的鎖對象是誰呢?
類的字節(jié)碼文件對象。
我們使用 synchronized 改進(jìn)我們上面的程序,前面線程安全的問題,
public class SellTickets implements Runnable { private int tickets = 100; //創(chuàng)建鎖對象 //把這個(gè)關(guān)鍵的鎖對象定義到run()方法(獨(dú)立于線程之外),造成同一把鎖 private Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"); } } } } }(八) lock鎖的概述和使用
為了更清晰的表達(dá)如何加鎖和釋放鎖,JDK5以后提供了一個(gè)新的鎖對象Lock
(可以更清晰的看到在哪里加上了鎖,在哪里釋放了鎖,)
void lock() 加鎖 void unlock() 釋放鎖
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SellTickets2 implements Runnable { private int tickets = 100; private Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock(); ; if (tickets > 0) { try { Thread.sleep(150); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"); } } finally { lock.unlock(); } } } }(九) 死鎖問題 (簡單認(rèn)識)
同步弊端
效率低
如果出現(xiàn)了同步嵌套,就容易產(chǎn)生死鎖問題
死鎖問題
是指兩個(gè)或者兩個(gè)以上的線程在執(zhí)行的過程中,因爭奪資源產(chǎn)生的一種互相等待現(xiàn)象
(十) 等待喚醒機(jī)制我們前面假定的電影院場景,其實(shí)還是有一定局限的,我們所假定的票數(shù)是一定的,但是實(shí)際生活中,往往是一種供需共存的狀態(tài),例如去買早點(diǎn),當(dāng)消費(fèi)者買走一些后,而作為生產(chǎn)者的店家就會補(bǔ)充一些商品,為了研究這一種場景,我們所要學(xué)習(xí)的就是Java的等待喚醒機(jī)制
生產(chǎn)者消費(fèi)者問題(英語:Producer-consumer problem),也稱有限緩沖問題(英語:Bounded-buffer problem),是一個(gè)多進(jìn)程同步問題的經(jīng)典案例。該問題描述了共享固定大小緩沖區(qū)的兩個(gè)進(jìn)程——即所謂的“生產(chǎn)者”和“消費(fèi)者”——在實(shí)際運(yùn)行時(shí)會發(fā)生的問題。生產(chǎn)者的主要作用是生成一定量的數(shù)據(jù)放到緩沖區(qū)中,然后重復(fù)此過程。與此同時(shí),消費(fèi)者也在緩沖區(qū)消耗這些數(shù)據(jù)。該問題的關(guān)鍵就是要保證生產(chǎn)者不會在緩沖區(qū)滿時(shí)加入數(shù)據(jù),消費(fèi)者也不會在緩沖區(qū)中空時(shí)消耗數(shù)據(jù)。
我們用通俗一點(diǎn)的話來解釋一下這個(gè)問題
Java使用的是搶占式調(diào)度模型
A:如果消費(fèi)者先搶到了CPU的執(zhí)行權(quán),它就會去消費(fèi)數(shù)據(jù),但是現(xiàn)在的數(shù)據(jù)是默認(rèn)值,如果沒有意義,應(yīng)該等數(shù)據(jù)有意義再消費(fèi)。就好比買家進(jìn)了店鋪早點(diǎn)卻還沒有做出來,只能等早點(diǎn)做出來了再消費(fèi)
B:如果生產(chǎn)者先搶到CPU的執(zhí)行權(quán),它就回去生產(chǎn)數(shù)據(jù),但是,當(dāng)它產(chǎn)生完數(shù)據(jù)后,還繼續(xù)擁有執(zhí)行權(quán),它還能繼續(xù)產(chǎn)生數(shù)據(jù),這是不合理的,你應(yīng)該等待消費(fèi)者將數(shù)據(jù)消費(fèi)掉,再進(jìn)行生產(chǎn)。 這又好比,店鋪不能無止境的做早點(diǎn),賣一些,再做,避免虧本
梳理思路:
A:生產(chǎn)者 —— 先看是否有數(shù)據(jù),有就等待,沒有就生產(chǎn),生產(chǎn)完之后通知消費(fèi)者來消費(fèi)數(shù)據(jù)
B:消費(fèi)者 —— 先看是否有數(shù)據(jù),有就消費(fèi),沒有就等待,通知生產(chǎn)者生產(chǎn)數(shù)據(jù)
解釋:喚醒——讓線程池中的線程具備執(zhí)行資格
Object類提供了三個(gè)方法:
//等待 wait() //喚醒單個(gè)線程 notify() //喚醒所有線程 notifyAll()
注意:這三個(gè)方法都必須在同步代碼塊中執(zhí)行 (例如synchronized塊),同時(shí)在使用時(shí)必須標(biāo)明所屬鎖,這樣才可以得出這些方法操作的到底是哪個(gè)鎖上的線程
為什么這些方法不定義在Thread類中呢 ?
這些方法的調(diào)用必須通過鎖對象調(diào)用,而我們剛才使用的鎖對象是任意鎖對象。
所以,這些方法必須定義在Object類中。
我們來寫一段簡單的代碼實(shí)現(xiàn)等待喚醒機(jī)制
public class Student { String name; int age; boolean flag;// 默認(rèn)情況是沒有數(shù)據(jù)(false),如果是true,說明有數(shù)據(jù) public Student() { } }
public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true){ synchronized (s) { //判斷有沒有數(shù)據(jù) //如果有數(shù)據(jù),就wait if (s.flag) { try { s.wait(); //t1等待,釋放鎖 } catch (InterruptedException e) { e.printStackTrace(); } } //沒有數(shù)據(jù),就生產(chǎn)數(shù)據(jù) if (x % 2 == 0) { s.name = "admin"; s.age = 20; } else { s.name = "User"; s.age = 30; } x++; //現(xiàn)在數(shù)據(jù)就已經(jīng)存在了,修改標(biāo)記 s.flag = true; //喚醒線程 //喚醒t2,喚醒并不表示你立馬可以執(zhí)行,必須還得搶CPU的執(zhí)行權(quán)。 s.notify(); } } } }
package cn.bwh_05_Notify; public class GetThread implements Runnable { private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true){ synchronized (s){ //如果沒有數(shù)據(jù),就等待 if (!s.flag){ try { s.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(s.name + "---" + s.age); //修改標(biāo)記 s.flag = false; //喚醒線程t1 s.notify(); } } } }
package cn.bwh_05_Notify; public class StudentTest { public static void main(String[] args) { Student s = new Student(); //設(shè)置和獲取的類 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); //線程類 Thread t1 = new Thread(st); Thread t2 = new Thread(gt); //啟動(dòng)線程 t1.start(); t2.start(); } } //運(yùn)行結(jié)果依次交替出現(xiàn)
生產(chǎn)者消費(fèi)者之等待喚醒機(jī)制代碼優(yōu)化
最終版代碼(在Student類中有大改動(dòng),然后GetThread類和SetThread類簡潔很多)
public class Student { private String name; private int age; private boolean flag; public synchronized void set(String name, int age) { if (this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.name = name; this.age = age; this.flag = true; this.notify(); } public synchronized void get() { if (!this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(this.name + "---" + this.age); this.flag = false; this.notify(); } }
public class SetThread implements Runnable { private Student s; private int x = 0; public SetThread(Student s) { this.s = s; } @Override public void run() { while (true) { if (x % 2 == 0) { s.set("admin", 20); } else { s.set("User", 30); } x++; } } }
public class GetThread implements Runnable{ private Student s; public GetThread(Student s) { this.s = s; } @Override public void run() { while (true){ s.get(); } } }
public class StudentTest { public static void main(String[] args) { Student s = new Student(); //設(shè)置和獲取的類 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); Thread t1 = new Thread(st); Thread t2 = new Thread(gt); t1.start(); t2.start(); } }
最終版代碼特點(diǎn):
把Student的成員變量給私有的了。
把設(shè)置和獲取的操作給封裝成了功能,并加了同步。
設(shè)置或者獲取的線程里面只需要調(diào)用方法即可
(十一) 線程池程序啟動(dòng)一個(gè)新線程成本是比較高的,因?yàn)樗婕暗揭c操作系統(tǒng)進(jìn)行交互。而使用線程池可以很好的提高性能,尤其是當(dāng)程序中要?jiǎng)?chuàng)建大量生存期很短的線程時(shí),更應(yīng)該考慮使用線程池
線程池里的每一個(gè)線程代碼結(jié)束后,并不會死亡,而是再次回到線程池中成為空閑狀態(tài),等待下一個(gè)對象來使用
在JDK5之前,我們必須手動(dòng)實(shí)現(xiàn)自己的線程池,從JDK5開始,Java內(nèi)置支持線程池
JDK5新增了一個(gè)Executors工廠類來產(chǎn)生線程池,有如下幾個(gè)方法 //創(chuàng)建一個(gè)具有緩存功能的線程池 //緩存:百度瀏覽過的信息再次訪問 public static?ExecutorService newCachedThreadPool() //創(chuàng)建一個(gè)可重用的,具有固定線程數(shù)的線程池 public static?ExecutorService newFixedThreadPool(intnThreads) ?? //創(chuàng)建一個(gè)只有單線程的線程池,相當(dāng)于上個(gè)方法的參數(shù)是1? public static?ExecutorService newSingleThreadExecutor() ?? 這些方法的返回值是ExecutorService對象,該對象表示一個(gè)線程池,可以執(zhí)行Runnable對象或者Callable對象代表的線程。它提供了如下方法 Future> submit(Runnable task)Future submit(Callable task)
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorDemo { public static void main(String[] args) { //創(chuàng)建一個(gè)線程池對象,控制要?jiǎng)?chuàng)建幾個(gè)線程對象 ExecutorService pool = Executors.newFixedThreadPool(2); //可以執(zhí)行Runnalble對象或者Callable對象代表的線程 pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); //結(jié)束線程池 pool.shutdown(); } }(十二) 匿名內(nèi)部類的方式實(shí)現(xiàn)多線程程序
匿名內(nèi)部類的格式:
new 類名或者接口名( ) { 重寫方法; };
本質(zhì):是該類或者接口的子類對象
public class ThreadDemo { public static void main(String[] args) { new Thread() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + i); } } }.start(); } }
public class RunnableDemo { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + i); } } }).start(); } }(十三) 定時(shí)器
定時(shí)器是一個(gè)應(yīng)用十分廣泛的線程工具,可用于調(diào)度多個(gè)定時(shí)任務(wù)以后臺線程的方式執(zhí)行。在Java中,可以通過Timer和TimerTask類來實(shí)現(xiàn)定義調(diào)度的功能
Timer
·public Timer() public void schedule(TimerTask task, long delay) public void schedule(TimerTask task,long delay,long period)
TimerTask
abstract void run() public boolean cancel()
開發(fā)中
Quartz是一個(gè)完全由java編寫的開源調(diào)度框架
結(jié)尾:如果內(nèi)容中有什么不足,或者錯(cuò)誤的地方,歡迎大家給我留言提出意見, 蟹蟹大家 !^_^
如果能幫到你的話,那就來關(guān)注我吧!(系列文章均會在公眾號第一時(shí)間更新)
在這里的我們素不相識,卻都在為了自己的夢而努力 ?一個(gè)堅(jiān)持推送原創(chuàng)Java技術(shù)的公眾號:理想二旬不止
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/75448.html
摘要:線程可以被稱為輕量級進(jìn)程。一個(gè)守護(hù)線程是在后臺執(zhí)行并且不會阻止終止的線程。其他的線程狀態(tài)還有,和。上下文切換是多任務(wù)操作系統(tǒng)和多線程環(huán)境的基本特征。在的線程中并沒有可供任何對象使用的鎖和同步器。 原文:Java Multi-Threading and Concurrency Interview Questions with Answers 翻譯:并發(fā)編程網(wǎng) - 鄭旭東 校對:方騰飛 多...
摘要:多線程和并發(fā)問題是技術(shù)面試中面試官比較喜歡問的問題之一。線程可以被稱為輕量級進(jìn)程。一個(gè)守護(hù)線程是在后臺執(zhí)行并且不會阻止終止的線程。其他的線程狀態(tài)還有,和。上下文切換是多任務(wù)操作系統(tǒng)和多線程環(huán)境的基本特征。 多線程和并發(fā)問題是 Java 技術(shù)面試中面試官比較喜歡問的問題之一。在這里,從面試的角度列出了大部分重要的問題,但是你仍然應(yīng)該牢固的掌握J(rèn)ava多線程基礎(chǔ)知識來對應(yīng)日后碰到的問題。(...
摘要:但是單核我們還是要應(yīng)用多線程,就是為了防止阻塞。多線程可以防止這個(gè)問題,多條線程同時(shí)運(yùn)行,哪怕一條線程的代碼執(zhí)行讀取數(shù)據(jù)阻塞,也不會影響其它任務(wù)的執(zhí)行。 1、多線程有什么用?一個(gè)可能在很多人看來很扯淡的一個(gè)問題:我會用多線程就好了,還管它有什么用?在我看來,這個(gè)回答更扯淡。所謂知其然知其所以然,會用只是知其然,為什么用才是知其所以然,只有達(dá)到知其然知其所以然的程度才可以說是把一個(gè)知識點(diǎn)...
摘要:大多數(shù)待遇豐厚的開發(fā)職位都要求開發(fā)者精通多線程技術(shù)并且有豐富的程序開發(fā)調(diào)試優(yōu)化經(jīng)驗(yàn),所以線程相關(guān)的問題在面試中經(jīng)常會被提到。掌握了這些技巧,你就可以輕松應(yīng)對多線程和并發(fā)面試了。進(jìn)入等待通行準(zhǔn)許時(shí),所提供的對象。 最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒有答案。 不管你是新程序員還是老手,你一定在面試中遇到過有關(guān)線程的問題。Java語言一個(gè)重要的特點(diǎn)就...
摘要:超詳細(xì)的面試題總結(jié)一之基本知識多線程和虛擬機(jī)創(chuàng)建線程有幾種不同的方式你喜歡哪一種為什么繼承類實(shí)現(xiàn)接口應(yīng)用程序可以使用框架來創(chuàng)建線程池實(shí)現(xiàn)接口。死亡線程方法執(zhí)行結(jié)束,或者因異常退出了方法,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。 超詳細(xì)的Java面試題總結(jié)(一)之Java基本知識 多線程和Java虛擬機(jī) 創(chuàng)建線程有幾種不同的方式?你喜歡哪一種?為什么? 繼承Thread類 實(shí)現(xiàn)R...
摘要:下面是線程相關(guān)的熱門面試題,你可以用它來好好準(zhǔn)備面試。線程安全問題都是由全局變量及靜態(tài)變量引起的。持有自旋鎖的線程在之前應(yīng)該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒有答案。 不管你是新程序員還是老手,你一定在面試中遇到過有關(guān)線程的問題。Java語言一個(gè)重要的特點(diǎn)就是內(nèi)置了對并發(fā)的支持,讓Java大受企業(yè)和程序員...
閱讀 2862·2021-07-30 15:30
閱讀 560·2019-08-30 15:55
閱讀 1625·2019-08-26 17:04
閱讀 637·2019-08-26 11:36
閱讀 2075·2019-08-26 10:58
閱讀 3554·2019-08-23 14:34
閱讀 1561·2019-08-22 18:48
閱讀 2529·2019-08-21 17:51