摘要:是不能直接調(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)先級的范圍是。
第五階段 多線程 前言:
(一) 線程和進(jìn)程的概述 (1) 進(jìn)程</>復(fù)制代碼
一個(gè)場景:周末,帶著并不存在的女票去看電影,無論是現(xiàn)場買票也好,又或是手機(jī)買票也好,上一秒還有位置,遲鈍了一下以后,就顯示該座位已經(jīng)無法選中,一不留神就沒有座位了,影院的票是一定的,但是究竟是如何做到,多個(gè)窗口或者用戶同時(shí)出票而又不重復(fù)的呢? 這就是我們今天所要講解的多線程問題
進(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)線程
</>復(fù)制代碼
public class MyThread extends Thread{
public MyThread() {
}
@Override
public void run() {
for (int i = 0; i < 100; i++){
System.out.println(getName() + ":" + i);
}
}
}
</>復(fù)制代碼
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ù)傳遞
</>復(fù)制代碼
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);
}
}
}
</>復(fù)制代碼
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è)置線程對象</>復(fù)制代碼
//獲取線程的名稱
public?final?String?getName()
//設(shè)置線程的名稱
public?final?void?setName(String?name)
設(shè)置線程的名稱 (如果不設(shè)置名稱的話,默認(rèn)是Thread-??(編號) )
方法一:無參構(gòu)造 + setXxx (推薦)
</>復(fù)制代碼
//創(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)造方法,方法一不用)
</>復(fù)制代碼
//MyThread類中
public MyThread(String name){
super(name);//直接調(diào)用父類的就好
}
//MyThreadTest類中
MyThread my = new MyThread("admin");
獲取線程名稱
注意:重寫run方法內(nèi)獲取線程名稱的方式
</>復(fù)制代碼
//Thread
getName()
//Runnable
//由于實(shí)現(xiàn)接口的方式不能直接使用Thread類的方法了,但是可以間接的使用
Thread.currentThread().getName()
使用實(shí)現(xiàn)Runnable接口方法的時(shí)候注意:main方法所在的測試類并不繼承Thread類,因此并不能直接使用getName()方法來獲取名稱。
</>復(fù)制代碼
//這種情況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)先級
</>復(fù)制代碼
假如我們的計(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)度模型
</>復(fù)制代碼
//演示如何設(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í)候才能看到比較好的效果。
(五) 線程控制在后面的案例中會用到一些,這些控制功能不是很難,可以自行測試。
</>復(fù)制代碼
//線程休眠
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)行完畢,線程消亡
(七) 多線程電影院出票案例</>復(fù)制代碼
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--) + "張票");
}
}
}
}
</>復(fù)制代碼
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ī)制
</>復(fù)制代碼
//同步代碼塊:
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)我們上面的程序,前面線程安全的問題,
</>復(fù)制代碼
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
(可以更清晰的看到在哪里加上了鎖,在哪里釋放了鎖,)
</>復(fù)制代碼
void lock() 加鎖
void unlock() 釋放鎖
</>復(fù)制代碼
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ī)制
</>復(fù)制代碼
生產(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è)方法:
</>復(fù)制代碼
//等待
wait()
//喚醒單個(gè)線程
notify()
//喚醒所有線程
notifyAll()
注意:這三個(gè)方法都必須在同步代碼塊中執(zhí)行 (例如synchronized塊),同時(shí)在使用時(shí)必須標(biāo)明所屬鎖,這樣才可以得出這些方法操作的到底是哪個(gè)鎖上的線程
為什么這些方法不定義在Thread類中呢 ?
這些方法的調(diào)用必須通過鎖對象調(diào)用,而我們剛才使用的鎖對象是任意鎖對象。
所以,這些方法必須定義在Object類中。
我們來寫一段簡單的代碼實(shí)現(xiàn)等待喚醒機(jī)制
</>復(fù)制代碼
public class Student {
String name;
int age;
boolean flag;// 默認(rèn)情況是沒有數(shù)據(jù)(false),如果是true,說明有數(shù)據(jù)
public Student() {
}
}
</>復(fù)制代碼
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();
}
}
}
}
</>復(fù)制代碼
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();
}
}
}
}
</>復(fù)制代碼
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類簡潔很多)
</>復(fù)制代碼
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();
}
}
</>復(fù)制代碼
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++;
}
}
}
</>復(fù)制代碼
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true){
s.get();
}
}
}
</>復(fù)制代碼
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)置支持線程池
</>復(fù)制代碼
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)
</>復(fù)制代碼
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)部類的格式:
</>復(fù)制代碼
new 類名或者接口名( ) {
重寫方法;
};
本質(zhì):是該類或者接口的子類對象
</>復(fù)制代碼
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();
}
}
</>復(fù)制代碼
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
</>復(fù)制代碼
·public Timer()
public void schedule(TimerTask task, long delay)
public void schedule(TimerTask task,long delay,long period)
TimerTask
</>復(fù)制代碼
abstract void run()
public boolean cancel()
開發(fā)中
Quartz是一個(gè)完全由java編寫的開源調(diào)度框架
結(jié)尾:如果內(nèi)容中有什么不足,或者錯(cuò)誤的地方,歡迎大家給我留言提出意見, 蟹蟹大家 !^_^
如果能幫到你的話,那就來關(guān)注我吧!(系列文章均會在公眾號第一時(shí)間更新)
</>復(fù)制代碼
在這里的我們素不相識,卻都在為了自己的夢而努力 ?
一個(gè)堅(jiān)持推送原創(chuàng)Java技術(shù)的公眾號:理想二旬不止
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://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