一、進(jìn)程和線(xiàn)程的概念
- 進(jìn)程:運(yùn)行中的某個(gè)程序或者應(yīng)用,至少包含一個(gè)線(xiàn)程
- 線(xiàn)程:進(jìn)程中負(fù)責(zé)執(zhí)行的一個(gè)或者多個(gè)執(zhí)行單元,歸屬于進(jìn)程,且多個(gè)線(xiàn)程共享進(jìn)程的資源
- 并發(fā)機(jī)制:多個(gè)線(xiàn)程同時(shí)運(yùn)行,CPU給每個(gè)線(xiàn)程分配時(shí)間片,獲得時(shí)間的線(xiàn)程運(yùn)行,其他線(xiàn)程等待,由于時(shí)間片很短,從宏觀上看是線(xiàn)程都在運(yùn)行,微觀上看是線(xiàn)程走走停停
二、線(xiàn)程的創(chuàng)建
1、繼承Thread類(lèi),重寫(xiě)run()
public class MyThread extends Thread{
private String name;
MyThread(String name){
this.name=name;
}
@Override
public void run(){
System.out.println("name"+name+",此線(xiàn)程的id為"+Thread.currentThread().getId());
}
public static void main(String[] args) {
System.out.println("當(dāng)前主線(xiàn)程id為:"+Thread.currentThread().getId());
MyThread myThread1 = new MyThread("線(xiàn)程1");
myThread1.start();
MyThread myThread2 = new MyThread("線(xiàn)程2");
myThread2.run();
}
}
輸出如下:
小結(jié):
- start()和run()方法的不同。start是啟動(dòng)線(xiàn)程,交給CPU去獲取時(shí)間片并調(diào)用run方法。而用run方法相當(dāng)于交給主線(xiàn)程去執(zhí)行run,并不會(huì)創(chuàng)建新的線(xiàn)程。
- 線(xiàn)程1是先創(chuàng)建的,但是輸出結(jié)果在后,說(shuō)明線(xiàn)程的創(chuàng)建并不會(huì)阻塞主線(xiàn)程的執(zhí)行
2、實(shí)現(xiàn)Runnable接口
public class MyRunnable implements Runnable {
private String name;
MyRunnable(String name){
this.name = name;
}
public void run() {
System.out.println("啟動(dòng)"+name+":id為"+Thread.currentThread().getId());
}
public static void main(String[] args) {
MyRunnable my = new MyRunnable("runnable線(xiàn)程");
Thread thread = new Thread(my);
thread.start();
}
}
3、最簡(jiǎn)潔的啟動(dòng)線(xiàn)程的方式
new Thread(new Runnable(){
public void run(){
xxxxxx
}
};).start();
三、線(xiàn)程的狀態(tài)
- 創(chuàng)建(new)狀態(tài): 準(zhǔn)備好了一個(gè)多線(xiàn)程的對(duì)象
- 就緒(runnable)狀態(tài): 調(diào)用了start()方法, 等待CPU進(jìn)行調(diào)度
- 運(yùn)行(running)狀態(tài): 執(zhí)行run()方法
- 阻塞(blocked)狀態(tài): 暫時(shí)停止執(zhí)行, 可能將資源交給其它線(xiàn)程使用
- 終止(dead)狀態(tài): 線(xiàn)程銷(xiāo)毀
1、當(dāng)線(xiàn)程進(jìn)入就緒狀態(tài)后,要等CPU分配到時(shí)間片之后,線(xiàn)程便真正進(jìn)入運(yùn)行狀態(tài)。
2、線(xiàn)程在運(yùn)行狀態(tài)過(guò)程中,可能有多個(gè)原因?qū)е庐?dāng)前線(xiàn)程不繼續(xù)運(yùn)行下去,比如用戶(hù)主動(dòng)讓線(xiàn)程睡眠(睡眠一定的時(shí)間之后再重新執(zhí)行)、用戶(hù)主動(dòng)讓線(xiàn)程等待,或者被同步塊給阻塞,此時(shí)就對(duì)應(yīng)著多個(gè)狀態(tài):time waiting(睡眠或等待一定的事件)、waiting(等待被喚醒)、blocked(阻塞)。
3、當(dāng)由于突然中斷或者子任務(wù)執(zhí)行完畢,線(xiàn)程就會(huì)被消亡。
注:sleep和wait的區(qū)別:
- sleep是Thread類(lèi)的方法,wait是Object類(lèi)中定義的方法.
- Thread.sleep不會(huì)導(dǎo)致鎖行為的改變, 如果當(dāng)前線(xiàn)程是擁有鎖的, 那么
Thread.sleep不會(huì)讓線(xiàn)程釋放鎖. - Thread.sleep和Object.wait都會(huì)暫停當(dāng)前的線(xiàn)程. OS會(huì)將執(zhí)行時(shí)間分配給其它線(xiàn)程. 區(qū)別是, 調(diào)用wait后, 需要?jiǎng)e的線(xiàn)程執(zhí)行notify/notifyAll才能夠重新獲得CPU執(zhí)行時(shí)間.
四、線(xiàn)程API
1、sleep():線(xiàn)程休眠,且不會(huì)釋放鎖。
public class MyThreadAPI{
private int i = 0;
private Object object = new Object();
public static void main(String[] args) {
MyThreadAPI api = new MyThreadAPI();
MyThreadSleep thread1 = api.new MyThreadSleep() ;
MyThreadSleep thread2 = api.new MyThreadSleep() ;
thread1.start();
thread2.start();
}
class MyThreadSleep extends Thread{
public void run(){
synchronized (object) {
i++;
System.out.println(Thread.currentThread().getName()+"睡眠前i:"+i);
System.out.println(Thread.currentThread().getName()+"進(jìn)入睡眠");
try {
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"睡眠結(jié)束");
i++;
System.out.println(Thread.currentThread().getName()+"睡眠后i:"+i);
}
}
}
}
輸出結(jié)果:
2、yield(),讓出當(dāng)前時(shí)間片,與sleep不同的是不可選擇時(shí)間
public class MyThreadYeild extends Thread {
public void run(){
long start = System.currentTimeMillis();
int count = 0;
for(int i = 1;i<50000000;i++){
count++;
Thread.yield();
}
System.out.println("當(dāng)前count為:"+count);
long end = System.currentTimeMillis();
System.out.println("耗時(shí):"+(end-start)+"毫秒");
}
public static void main(String[] args) {
MyThreadYeild thread = new MyThreadYeild();
thread.start();
}
}
輸出可發(fā)現(xiàn):不寫(xiě)thread.yield()方法時(shí),執(zhí)行時(shí)間較短
線(xiàn)程的sleep()方法和yield()方法有什么區(qū)別?
① sleep()方法給其他線(xiàn)程運(yùn)行機(jī)會(huì)時(shí)不考慮線(xiàn)程的優(yōu)先級(jí),因此會(huì)給低優(yōu)先級(jí)的線(xiàn)程以運(yùn)行的機(jī)會(huì);yield()方法只會(huì)給相同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線(xiàn)程以運(yùn)行的機(jī)會(huì);
② 線(xiàn)程執(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))具有更好的可移植性。
3、join()方法
主線(xiàn)程創(chuàng)建并啟動(dòng)了線(xiàn)程,如果子線(xiàn)程中要進(jìn)行大量耗時(shí)運(yùn)算,主線(xiàn)程往往將早于子線(xiàn)程結(jié)束之前結(jié)束。這時(shí),如果主線(xiàn)程想等待子線(xiàn)程執(zhí)行完成之后再結(jié)束,比如子線(xiàn)程處理一個(gè)數(shù)據(jù),主線(xiàn)程要取得這個(gè)數(shù)據(jù)中的值,就要用到j(luò)oin()方法了。方法join()的作用是等待線(xiàn)程對(duì)象銷(xiāo)毀。
public class MyThread4 extends Thread {
public MyThread4(String name){
super(name);
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) throws InterruptedException {
// 啟動(dòng)子進(jìn)程
for (int i = 0; i < 10; i++) {
if (i == 5) {
MyThread4 th = new MyThread4("joined thread");
th.start();
th.join();
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
輸出如下:
4、interrupt線(xiàn)程中斷
public void interrupt(); 中斷線(xiàn)程。
public static boolean interrupted(); 是一個(gè)靜態(tài)方法,用于測(cè)試當(dāng)前線(xiàn)程是否已經(jīng)中斷,并將線(xiàn)程的中斷狀態(tài) 清除。所以如果線(xiàn)程已經(jīng)中斷,調(diào)用兩次interrupted,第二次時(shí)會(huì)返回false,因?yàn)榈谝淮畏祷豻rue后會(huì)清除中斷狀態(tài)。
五、守護(hù)線(xiàn)程
線(xiàn)程分為用戶(hù)線(xiàn)程和守護(hù)線(xiàn)程,當(dāng)用戶(hù)線(xiàn)程結(jié)束,守護(hù)線(xiàn)程會(huì)被強(qiáng)制終止。GC就是守護(hù)線(xiàn)程,默認(rèn)線(xiàn)程是用戶(hù)線(xiàn)程,在啟動(dòng)前可設(shè)置為守護(hù)