多線(xiàn)程
進(jìn)程和線(xiàn)程的關(guān)系姨丈,舉個(gè)形象的例子戳杀。360安全衛(wèi)士就是一個(gè)進(jìn)程,QQ是另外一個(gè)進(jìn)程。在360中俩莽,我們可以同時(shí)進(jìn)行電腦體檢和電腦清理困介,這兩個(gè)功能模塊就是兩個(gè)線(xiàn)程,他們并發(fā)執(zhí)行。
實(shí)現(xiàn)多線(xiàn)程的方法
繼承Thread類(lèi)
Thread類(lèi)中有run()
方法辨赐,這個(gè)方法就是封裝自定義線(xiàn)程運(yùn)行任務(wù)的函數(shù),一般覆蓋run()京办。步驟如下
- 定義一個(gè)類(lèi)繼承Thread
- 覆蓋run方法
- 創(chuàng)建Thread子類(lèi)對(duì)象創(chuàng)建線(xiàn)程
- 調(diào)用start方法
public class Print extends Thread {
private int num;
Print(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println("Thread" + num + " Run");
}
}
class ThreadDemo {
public static void main(String[] args) {
// 注意一個(gè)線(xiàn)程只能被start()一次
while (true) {
new Print(1).start();
new Print(2).start();
}
}
}
- CPU執(zhí)行資格:可以被CPU處理掀序,在處理隊(duì)列中排隊(duì)。
- CPU執(zhí)行權(quán):正在被CPU處理惭婿。
- 線(xiàn)程凍結(jié)時(shí):放棄了執(zhí)行權(quán)也放棄了執(zhí)行資格不恭。
- 臨時(shí)阻塞:加入甲乙丙都有執(zhí)行資格,而只有甲有執(zhí)行權(quán)财饥,此時(shí)乙丙就出于臨時(shí)阻塞狀態(tài)换吧,有執(zhí)行資格,無(wú)執(zhí)行權(quán)钥星。
實(shí)現(xiàn)Runnable
若是幾個(gè)線(xiàn)程共用一個(gè)數(shù)據(jù)沾瓦,繼承自Thread就不行了,因?yàn)槊總€(gè)子類(lèi)都有自己的數(shù)據(jù)谦炒。這時(shí)候需要實(shí)現(xiàn)Runnable贯莺。比較以下例子。
public class Bank extrends Thread {
private int money = 100;
@Override
public void run() {
money--;
System.out.println(money);
}
public static void main(String[] args) {
new Bank().start();
new Bank().start();
new Bank().start();
new Bank().start();
// 這里打印四個(gè)99宁改,說(shuō)明每個(gè)線(xiàn)程用的都是自己的的money
}
}
public class Bank implements Runnable {
private int money = 100;
@Override
public void run() {
money--;
System.out.println(money);
}
public static void main(String[] args) {
Bank a = new Bank();
new Thread(a).start();
new Thread(a).start();
new Thread(a).start();
new Thread(a).start();
// 傳入某個(gè)特定的Bank缕探,這里打印99,98还蹲,97爹耗,96,實(shí)現(xiàn)Runnable多了線(xiàn)程可以共用一個(gè)數(shù)據(jù)
}
}
實(shí)現(xiàn)Runnable接口
覆蓋run方法
-
通過(guò)Thread類(lèi)創(chuàng)建子對(duì)象谜喊,并將Runnable接口子類(lèi)作為T(mén)hread類(lèi)的構(gòu)造函數(shù)參數(shù)進(jìn)行傳遞
Demo d = new Demo();
Thread t1 = new Thread(d);
調(diào)用start方法
實(shí)現(xiàn)Runnable接口的好處
- 幾個(gè)線(xiàn)程可以共用同一份數(shù)據(jù)
- 避免了Java單繼承的局限潭兽,比如
class Demo extends Fu implements Runnable
;一般開(kāi)發(fā)中用這種方式較多。
線(xiàn)程安全
產(chǎn)生原因
- 多個(gè)線(xiàn)程在共用同一個(gè)數(shù)據(jù)時(shí)
- 操作共享數(shù)據(jù)的線(xiàn)程代碼有多余斗遏,當(dāng)一個(gè)線(xiàn)程在執(zhí)行操作共享數(shù)據(jù)的多余代碼的過(guò)程中山卦,其他線(xiàn)程又參與運(yùn)算,就會(huì)導(dǎo)致線(xiàn)程安全最易。
解決辦法
解決上述問(wèn)題的方法怒坯,可以封裝這些多余代碼炫狱,當(dāng)一個(gè)線(xiàn)程執(zhí)行時(shí)藻懒,其他線(xiàn)程不能進(jìn)入。
synchronized代碼塊
// 這里需要傳入一個(gè)對(duì)象视译,即同步鎖嬉荆,可以看成一個(gè)標(biāo)志位,有一個(gè)線(xiàn)程進(jìn)來(lái)了酷含,就從1 -> 0鄙早,其他線(xiàn)程進(jìn)不去汪茧,等進(jìn)入的線(xiàn)程執(zhí)行完畢后,標(biāo)志位再?gòu)? -> 1
syschronized(Object) {
// 需要封裝的多余代碼
}
同步
- 同步的好處:課解決線(xiàn)程安全問(wèn)題
- 同步的缺點(diǎn):效率低限番,因?yàn)橥酵獾木€(xiàn)程都會(huì)判斷同步鎖
- 同步的前提:多個(gè)線(xiàn)程用了共享的數(shù)據(jù)且共用一把同步鎖舱污,作為鎖的這個(gè)對(duì)象必須在run()方法之外。
同步函數(shù)
// 同步鎖去哪兒了弥虐?這里默認(rèn)用的是this
public synchronized void func() {
}
同步代碼塊可以使用任意對(duì)象作為鎖扩灯,而同步函數(shù)只能用this。
若同步函數(shù)是static的霜瘪,用的鎖是該函數(shù)所屬的字節(jié)碼文件對(duì)象珠插,可以用類(lèi)名.class
獲取,不能用this.getClass()
因?yàn)殪o態(tài)函數(shù)加載的時(shí)候還沒(méi)有對(duì)象
死鎖
- 嵌套鎖或造成死鎖颖对,即一個(gè)線(xiàn)程拿走了另外一個(gè)線(xiàn)程的鎖捻撑。
- 所有線(xiàn)程都在線(xiàn)程池也會(huì)死鎖。
進(jìn)程間通信:wait()將線(xiàn)程凍結(jié)缤底,存儲(chǔ)到線(xiàn)程池中等待顾患。notify()會(huì)喚醒線(xiàn)程池中任一個(gè)線(xiàn)程,若本方喚醒了自己个唧,沒(méi)有意義描验,while+notify會(huì)導(dǎo)致死鎖。while+notifyAll可解決坑鱼。
wait()和sleep()
sleep()需要指定時(shí)間膘流,毫秒級(jí)。< -- > wait()課指定時(shí)間也可不指定鲁沥。
sleep()釋放執(zhí)行權(quán)呼股,不釋放鎖。< -- > wait()釋放執(zhí)行權(quán)也釋放鎖画恰。
多線(xiàn)程新特性
以下代碼和synchronized代碼塊作用類(lèi)似彭谁。注意try里是瞎寫(xiě)的,只是為了演示新特性的新方法允扇。
Lock lock = new ReentrantLock();
// Condition可以和任意的鎖組合使用
Condition condition = lock.newCondition();
// 在這里上鎖
lock.lock();
try {
// 這三個(gè)函數(shù)相當(dāng)于wait()缠局、notify()、notifyAll()
condition.await();
condition.signal();
condition.signalAll();
// 最后要釋放鎖
} finally {
lock.unlock();
}
繼承自O(shè)bject的幾個(gè)方法
equals() {return this==other}
默認(rèn)是比較地址考润∠猎埃可以覆蓋,比如String類(lèi)糊治、Integer類(lèi)覆蓋后比較的是內(nèi)容或值唱矛。hashcode
兩個(gè)對(duì)象的哈希值相同,才能說(shuō)他們具有相同的地址。-
getClass()
绎谦,返回此Object的運(yùn)行時(shí)類(lèi)管闷,字節(jié)碼文件對(duì)象。Person p1 = new Person(); Person p2 = new Person(); // Class xx = p1.getClass(); p1.getClass() == P2.getClass() // true,兩個(gè)對(duì)象指向的類(lèi)Person
toString()
將對(duì)象轉(zhuǎn)換為字符串窃肠,如直接打印上面的p1
包个,其實(shí)默認(rèn)調(diào)用了p1.toString()
,其內(nèi)部實(shí)現(xiàn)實(shí)際是返回了多個(gè)由+
連接的字符串冤留。
Integer的一些方法
int a = Integer.parseInt("6");
// 其他進(jìn)制轉(zhuǎn)為十進(jìn)制赃蛛,第二個(gè)參數(shù)為進(jìn)制
int b = Integer.parseInt("110", 2);
// 十進(jìn)制轉(zhuǎn)為其他進(jìn)制
String c = Integer.toBinaryString(6);
String d = Integer.toHexString(10);
// 和parseInt和相似的方法,parse返回int搀菩,valueOf返回Integer對(duì)象
// 其他進(jìn)制轉(zhuǎn)十進(jìn)制
Integer aa = Integer.valueOf("110", 2);
by @sunhaiyu
2016.12.14