線程的概念及疑問(wèn)
- 進(jìn)程和線程
進(jìn)程是一個(gè)靜態(tài)的概念痘绎,機(jī)器上的一個(gè)class文件,程序要執(zhí)行糟秘,首先要把class文件加載進(jìn)內(nèi)存简逮,加載后并未執(zhí)行,進(jìn)程已經(jīng)產(chǎn)生了尿赚。進(jìn)程開始執(zhí)行是指主方法(main方法)執(zhí)行了散庶,實(shí)際上運(yùn)行的都是線程。 - 通過(guò)Runnable接口創(chuàng)建線程
public class TestThread1 {
public static void main(String args[]) {
Runner r = new Runner();
Thread t = new Thread(r); //主線程;
t.start(); //啟動(dòng)分支線程
for(int i=0; i<100; i++) {
System.out.println("Main Thread:----" + i);
}
}
}
class Runner implements Runnable {
public void run() { //線程運(yùn)行體;
for(int i=0; i<100; i++) {
System.out.println("Runner1 :" + i);
}
}
}
- 繼承Thread類創(chuàng)建線程
public class TestThread1_2{
public static void main(String arg[]){
Runner1_2 r = new Runner1_2();
r.start();
for (int i = 0 ;i <= 100; i++){
System.out.println("Main Thread:!!!!"+i);
}
}
}
class Runner1_2 extends Thread{
public void run(){
for (int i = 0 ;i <= 100; i++){
System.out.println("Runner2:!!!!"+i);
}
}
}
- 上面2個(gè)程序運(yùn)行結(jié)果的分析
上面2個(gè)程序允許的時(shí)候會(huì)發(fā)現(xiàn)凌净,原先我們的程序都是看起來(lái)都是順序執(zhí)行的
悲龟,當(dāng)我們啟動(dòng)一個(gè)線程后會(huì)出現(xiàn)main線程和我們啟動(dòng)的新線程交替執(zhí)行的現(xiàn)象”埃可以看出CPU有一個(gè)調(diào)度的過(guò)程须教,多個(gè)線程都獲得了執(zhí)行的機(jī)會(huì)。 - 單獨(dú)執(zhí)行調(diào)用run方法是不會(huì)啟動(dòng)一個(gè)新的線程的斩芭。
public class TestThread1_1 {
public static void main(String args[]) {
Runner r = new Runner();
Thread t = new Thread(r);
t.run(); //這里不會(huì)啟動(dòng)一個(gè)分支線程轻腺,也不會(huì)有交替獲得時(shí)間片的情況
for(int i=0; i<100; i++) {
System.out.println("Main Thread:----" + i);
}
}
}
class Runner implements Runnable {
public void run() { //線程運(yùn)行體;
for(int i=0; i<100; i++) {
System.out.println("Runner1 :" + i);
}
}
}
線程的狀態(tài)
Thread states and life cycle in Java
(來(lái)源:https://www.uml-diagrams.org/java-thread-uml-state-machine-diagram-example.html)
線程狀態(tài)的文字描述
- New : 線程對(duì)象被創(chuàng)建后,就進(jìn)入了新建狀態(tài)划乖。例如贬养,Thread thread = new Thread()。
- Runnable:(2-1 Ready) 線程對(duì)象被創(chuàng)建后琴庵,其它線程調(diào)用了該對(duì)象的start()方法误算,從而來(lái)啟動(dòng)該線程仰美。例如,thread.start()儿礼。處于就緒狀態(tài)的線程咖杂,隨時(shí)可能被CPU調(diào)度執(zhí)行。 (2-2 Running)運(yùn)行狀態(tài)() : 線程獲取CPU的時(shí)間片蚊夫。需要注意的是诉字,線程只能從就緒狀態(tài)進(jìn)入到運(yùn)行狀態(tài)。
- 線程因?yàn)槟撤N原因放棄CPU使用權(quán)这橙,暫時(shí)停止運(yùn)行奏窑。直到線程進(jìn)入就緒狀態(tài)导披,才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)屈扎。
(https://dzone.com/articles/difference-between-blocked-waiting-timed-waiting-e 線程幾種狀態(tài)的解釋)
3.1 Blocked
Java doc formally defines BLOCKED state as: “A thread that is blocked waiting for a monitor lock is in this state.”
Real-life example: Today you are going for a job interview. This is your dream job, which you have been targeting for last few years. You woke up early in the morning, got ready, put on your best outfit, looking sharp in front of the mirror. Now you step out to your garage and realize that your wife has already taken the car. In this scenario, you only have one car, so what will happen? In real life, a fight may happen :-). Here you are BLOCKED because your wife has already taken the car. You won't be able to go to the interview.
This is the BLOCKED state. Explaining it in technical terms, you are the thread T1 and your wife is the thread T2 and lock is the car. T1 is BLOCKED on the lock (i.e. the car), because T2 has already acquired this lock.
Titbit: A Thread will enter in to BLOCKED state when it’s waiting for a monitor lock to enter a synchronized block/method or reenter a synchronized block/method after calling Object#wait() method.
3.2 Waiting
Java doc formally defines WAITING state as: “A thread that is waiting indefinitely for another thread to perform a particular action is in this state.”
Real-life example: Let’s say few minutes later your wife comes back home with the car. Now you realize that the interview time is approaching, and there is a long distance to drive to get there. So, you put all the power on the gas pedal in the car. You drive at 100 mph when the allowed speed limit is only 60 mph. Your luck, a traffic cop sees you driving over the speed limit, and he pulls you over to the curb. Now you are entering into the WAITING state, my friend. You stop driving the car and sit idly in the car until the cop investigates you, and then lets you go. Basically, until he lets you go, you are stuck in the WAITING state.
Explaining it in technical terms, you are thread T1 and the cop is thread T2. You released your lock (i.e. you stopped driving the car), and went into the WAITING state. Until the cop (i.e. T2) lets you go, you will be stuck in this WAITING state.
Titbit: A Thread will enter in to WAITING state when it’s calling one of the following methods:
Object#wait() with no timeout (沒有時(shí)間參數(shù))
Thread#join() with no timeout (沒有時(shí)間參數(shù))
LockSupport#park()
Thread that has called Object.wait() on an object is in WAITING state until another thread to call Object.notify() or Object.notifyAll() on that object. A thread that has called Thread.join() is in WAITING state for a specified thread to terminate.
3.3 Timed Waiting
Java doc formally defines TIMED_WAITING state as: “A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.”
Real-life example: Despite all the drama, you did extremely well in the interview, impressed everyone and got this high paying job. (Congratulations!) You come back home and tell to your neighbor about this new job and how excited you are about it. Your friend says that he is also working in the same office building. He suggests that the two of you should drive together. You think it’s a great idea. So on the first day of work, you go to his house. You stop your car in front of his house. You wait for 10 minutes, but your neighbor still doesn’t come out. You go ahead and start driving to work, as you don’t want to be delayed on your first day. Now this is TIMED_WAITING.
Explaining it in technical terms, you are thread T1 and your neighbor is thread T2. You release lock (i.e. stop driving the car) and wait up to 10 minutes. If your neighbor, T2, doesn’t come out in 10 minutes, you start driving the car again.
Titbit: A Thread will enter in to TIMED_WAITING state when it’s calling one of the following methods:
Thread#sleep()
Object#wait() with timeout
Thread#join() with timeout
LockSupport#parkNanos()
LockSupport#parkUntil()
3.4 Conclusion
When someone is analyzing thread dumps, understanding these different thread states are critical. How many threads are in RUNNABLE, BLOCKED, WAITING, and TIMED_WATING states? Which threads are blocked? Who is blocking them? What object used for locking? These are some of the important metrics to be analyzed in thread dumps. These kinds of detailed thread dump analyses can easily be done through an online tool such as: http://fastthread.io/
4.常見的幾種阻塞的描述
(01) 等待阻塞 -- 通過(guò)調(diào)用線程的wait()方法,讓線程等待某工作的完成撩匕。
(02) 同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因?yàn)殒i被其它線程所占用)鹰晨,它會(huì)進(jìn)入同步阻塞狀態(tài)。
(03) 其他阻塞 -- 通過(guò)調(diào)用線程的sleep()或join()或發(fā)出了I/O請(qǐng)求時(shí)止毕,線程會(huì)進(jìn)入到阻塞狀態(tài)模蜡。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)扁凛、或者I/O處理完畢時(shí)忍疾,線程重新轉(zhuǎn)入就緒狀態(tài)。
- 死亡狀態(tài)(terminated) : 線程執(zhí)行完了或者因異常退出了run()方法谨朝,該線程結(jié)束生命周期卤妒。
線程控制的基本方法
- isAlive() :判斷線程是否還活著,即線程是否還未中止字币。
- getPriority(),setPriority():獲取/設(shè)置線程的優(yōu)先級(jí)则披。
- Thread.sleep():設(shè)置當(dāng)前線程睡眠指定的毫秒數(shù)。
- join():調(diào)用某線程的該方法洗出,將當(dāng)前線程與該線程合并士复,即等待該線程結(jié)束,再恢復(fù)當(dāng)前線程的執(zhí)行翩活。
- yield():讓出CPU阱洪,當(dāng)前線程進(jìn)入就緒隊(duì)列等待調(diào)度。
- wait():當(dāng)前線程進(jìn)入對(duì)象的wait pool.
- notify(),notifyAll():?jiǎn)拘褜?duì)象的wait pool中的一個(gè)/所有等待線程菠镇。
- run()
- Thread.interrupt()
- stop()
sleep() & interrupt()
import java.util.*;
public class TestInterrupt {
public static void main(String arg[]){
MyThread thread = new MyThread();
thread.start();
try{
Thread.sleep(10000);
} //主線程睡眠…
catch(InterruptedException e){
}
thread.interrupt(); //中斷線程冗荸。
}
}
class MyThread extends Thread{
boolean flag = true;
public void run(){ //重寫的方法不能拋出不必被重寫方法不同的方法,此處不能寫throws InterruptedException
while(flag){
System.out.println("----"+ new Date()+"----");
try{
sleep(1000);
}
catch(InterruptedException e){ //捕獲拋出的異常
return; //停止
}
}
}
}
----Mon Jul 02 00:19:28 CST 2018----
----Mon Jul 02 00:19:29 CST 2018----
----Mon Jul 02 00:19:30 CST 2018----
----Mon Jul 02 00:19:31 CST 2018----
----Mon Jul 02 00:19:32 CST 2018----
----Mon Jul 02 00:19:33 CST 2018----
----Mon Jul 02 00:19:34 CST 2018----
----Mon Jul 02 00:19:35 CST 2018----
----Mon Jul 02 00:19:36 CST 2018----
----Mon Jul 02 00:19:37 CST 2018----
join
public class TestJoin {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("abcde");
t1.start();//啟動(dòng)分支線程
try {
t1.join();//把T1分支線程合并到當(dāng)前線程,這樣當(dāng)前線程會(huì)等待T1執(zhí)行完成再繼續(xù)執(zhí)行
} catch (InterruptedException e) {}
for(int i=1;i<=10;i++){
System.out.println("i am main thread");
}
}
}
class MyThread2 extends Thread {
MyThread2(String s){
super(s);
}
public void run(){
for(int i =1;i<=10;i++){
System.out.println("i am "+getName());
try {
sleep(1000); //制造一種一秒打印一次的效果
} catch (InterruptedException e) {
return;
}
}
}
}
i am abcde
i am abcde
i am abcde
i am abcde
i am abcde
i am abcde
i am abcde
i am abcde
i am abcde
i am abcde
i am main thread
i am main thread
i am main thread
i am main thread
i am main thread
i am main thread
i am main thread
i am main thread
i am main thread
i am main thread
將MyThread2 中run方法中的for循環(huán)的10改為100,然后會(huì)打印會(huì)所有的i am abcde辟犀,下面才會(huì)打印i am main thread俏竞。