一赃份、多線程的實(shí)現(xiàn)方式
編寫多線程程序一般有三種方法鸠姨,Thread,Runnable,Callable.
1).繼承Thread類
class MyThread extends Thread{
public MyThread(String name) {
}
public void run(){
}
}
2).實(shí)現(xiàn)Runnable接口
public interface Runnable {
public abstract void run();
}
3).實(shí)現(xiàn)Callable接口
public interface Callable<V> {
V call() throws Exception;
}
區(qū)別
- Thread不適合于資源的共享
- Thread類也是Runnable接口的子類铜秆。
- Callable能返回執(zhí)行結(jié)果,Runnable不能返回結(jié)果讶迁;
- Callable的call()方法允許拋出異常连茧,Runnable接口的run()方法的異常只能在內(nèi)部消化,不能繼續(xù)上拋巍糯;
Callable接口支持返回執(zhí)行結(jié)果啸驯,此時(shí)需要調(diào)用FutureTask.get()方法實(shí)現(xiàn),此方法會(huì)阻塞主線程直到獲取‘將來(lái)’結(jié)果鳞贷;當(dāng)不調(diào)用此方法時(shí)坯汤,主線程不會(huì)阻塞!
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ExecutorsTester {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
// executorService.execute(new ThreadForpools(1));
int a = 1, b = 100;
Future<Integer> future = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println( " 開始處理線程2罄ⅰ6枘簟!");
Thread.sleep(3000);
return a+b;
}
});
System.out.println(future.isDone());
System.out.println(future.isCancelled());
while (! future.isDone()) {
System.out.println("still waiting....");
Thread.sleep(1000 * 1);
// System.out.println("OK");
}
Integer obj = future.get();
System.out.println(obj);
executorService.shutdown();
}
}
二咱筛、線程的狀態(tài)
- 創(chuàng)建(new)狀態(tài): 準(zhǔn)備好了一個(gè)多線程的對(duì)象
- 就緒(runnable)狀態(tài): 調(diào)用了start()方法, 等待CPU進(jìn)行調(diào)度
- 運(yùn)行(running)狀態(tài): 執(zhí)行run()方法
- 阻塞(blocked)狀態(tài): 暫時(shí)停止執(zhí)行, 可能將資源交給其它線程使用
- 終止(dead)狀態(tài): 線程銷毀
當(dāng)需要新起一個(gè)線程來(lái)執(zhí)行某個(gè)子任務(wù)時(shí)搓幌,就創(chuàng)建了一個(gè)線程。但是線程創(chuàng)建之后迅箩,不會(huì)立即進(jìn)入就緒狀態(tài)溉愁,因?yàn)榫€程的運(yùn)行需要一些條件(比如內(nèi)存資源),只有線程運(yùn)行需要的所有條件滿足了饲趋,才進(jìn)入就緒狀態(tài)拐揭。
當(dāng)線程進(jìn)入就緒狀態(tài)后撤蟆,不代表立刻就能獲取CPU執(zhí)行時(shí)間,也許此時(shí)CPU正在執(zhí)行其他的事情堂污,因此它要等待家肯。當(dāng)?shù)玫紺PU執(zhí)行時(shí)間之后,線程便真正進(jìn)入運(yùn)行狀態(tài)盟猖。
線程在運(yùn)行狀態(tài)過(guò)程中讨衣,可能有多個(gè)原因?qū)е庐?dāng)前線程不繼續(xù)運(yùn)行下去,比如用戶主動(dòng)讓線程睡眠(睡眠一定的時(shí)間之后再重新執(zhí)行)式镐、用戶主動(dòng)讓線程等待反镇,或者被同步塊給阻塞,此時(shí)就對(duì)應(yīng)著多個(gè)狀態(tài):time waiting(睡眠或等待一定的事件)娘汞、waiting(等待被喚醒)歹茶、blocked(阻塞)。
當(dāng)由于突然中斷或者子任務(wù)執(zhí)行完畢价说,線程就會(huì)被消亡辆亏。
blocked、waiting鳖目、time waiting又統(tǒng)稱為阻塞狀態(tài)
注:sleep和wait的區(qū)別:
- sleep是Thread類的方法,wait是Object類中定義的方法.
- Thread.sleep不會(huì)導(dǎo)致鎖行為的改變, 如果當(dāng)前線程是擁有鎖的, 那么Thread.sleep不會(huì)讓線程釋放鎖.
Thread.sleep和Object.wait都會(huì)暫停當(dāng)前的線程. OS會(huì)將執(zhí)行時(shí)間分配給其它線程. 區(qū)別是, 調(diào)用wait后, 需要?jiǎng)e的線程執(zhí)行notify/notifyAll才能夠重新獲得CPU執(zhí)行時(shí)間.
線程的sleep()方法和yield()方法有什么區(qū)別?
- ① sleep()方法給其他線程運(yùn)行機(jī)會(huì)時(shí)不考慮線程的優(yōu)先級(jí)缤弦,因此會(huì)給低優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì)领迈;yield()方法只會(huì)給相同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì);
- ② 線程執(zhí)行sleep()方法后轉(zhuǎn)入阻塞(blocked)狀態(tài)碍沐,而執(zhí)行yield()方法后轉(zhuǎn)入就緒(ready)狀態(tài)狸捅;
- ③ sleep()方法聲明拋出InterruptedException,而yield()方法沒(méi)有聲明任何異常累提;
- ④ sleep()方法比yield()方法(跟操作系統(tǒng)CPU調(diào)度相關(guān))具有更好的可移植性尘喝。
請(qǐng)說(shuō)出與線程同步以及線程調(diào)度相關(guān)的方法。
wait():使一個(gè)線程處于等待(阻塞)狀態(tài)斋陪,并且釋放所持有的對(duì)象的鎖朽褪;
sleep():使一個(gè)正在運(yùn)行的線程處于睡眠狀態(tài),是一個(gè)靜態(tài)方法无虚,調(diào)用此方法要處理InterruptedException異常缔赠;
notify():?jiǎn)拘岩粋€(gè)處于等待狀態(tài)的線程,當(dāng)然在調(diào)用此方法的時(shí)候友题,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程嗤堰,而是由JVM確定喚醒哪個(gè)線程,而且與優(yōu)先級(jí)無(wú)關(guān)度宦;
notityAll():?jiǎn)拘阉刑幱诘却隣顟B(tài)的線程踢匣,該方法并不是將對(duì)象的鎖給所有線程告匠,而是讓它們競(jìng)爭(zhēng),只有獲得鎖的線程才能進(jìn)入就緒狀態(tài)离唬;
守護(hù)線程(Daemon Thread)和用戶線程(User Thread)的區(qū)別
Daemon的作用是為其他線程的運(yùn)行提供服務(wù)后专,比如說(shuō)GC線程。其實(shí)User Thread線程和Daemon Thread守護(hù)線程本質(zhì)上來(lái)說(shuō)去沒(méi)啥區(qū)別的男娄,唯一的區(qū)別之處就在虛擬機(jī)的離開:如果User Thread全部撤離行贪,那么Daemon Thread也就沒(méi)啥線程好服務(wù)的了,所以虛擬機(jī)也就退出了模闲。
守護(hù)線程依賴于創(chuàng)建它的線程建瘫,而用戶線程則不依賴。舉個(gè)簡(jiǎn)單的例子:如果在main線程中創(chuàng)建了一個(gè)守護(hù)線程尸折,當(dāng)main方法運(yùn)行完畢之后啰脚,守護(hù)線程也會(huì)隨著消亡。而用戶線程則不會(huì)实夹,用戶線程會(huì)一直運(yùn)行直到其運(yùn)行完畢橄浓。在JVM中,像垃圾收集器線程就是守護(hù)線程亮航。
三荸实、線程的優(yōu)先級(jí)
在操作系統(tǒng)中,線程可以劃分優(yōu)先級(jí)缴淋,優(yōu)先級(jí)較高的線程得到的CPU資源較多准给,也就是CPU優(yōu)先執(zhí)行優(yōu)先級(jí)較高的線程對(duì)象中的任務(wù)。
設(shè)置線程優(yōu)先級(jí)有助于幫“線程規(guī)劃器”確定在下一次選擇哪一個(gè)線程來(lái)優(yōu)先執(zhí)行重抖。
設(shè)置線程的優(yōu)先級(jí)使用setPriority()方法露氮,此方法在JDK的源碼如下:
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
在Java中,線程的優(yōu)先級(jí)分為1~10這10個(gè)等級(jí)钟沛,如果小于1或大于10畔规,則JDK拋出異常throw new IllegalArgumentException()。
線程優(yōu)先級(jí)特性:
- 繼承性:
比如A線程啟動(dòng)B線程恨统,則B線程的優(yōu)先級(jí)與A是一樣的叁扫。 - 規(guī)則性:
高優(yōu)先級(jí)的線程總是大部分先執(zhí)行完,但不代表高優(yōu)先級(jí)線程全部先執(zhí)行完延欠。 - 隨機(jī)性:
優(yōu)先級(jí)較高的線程不一定每一次都先執(zhí)行完陌兑。