常用方法
static Thread currentThread() 返回對當(dāng)前正在執(zhí)行的線程對象的引用。
long getId()返回該線程的標(biāo)識符。
String getName()返回該線程的名稱。
int getPriority() 返回線程的優(yōu)先級。
void interrupt() 中斷線程。
boolean isAlive()測試線程是否處于活動狀態(tài)。
void join()等待該線程終止。
void join(long millis)等待該線程終止的時間最長為 millis 毫秒。
void join(long millis, int nanos)等待該線程終止的時間最長為 millis 毫秒 + nanos 納秒。
void setDaemon(boolean on)將該線程標(biāo)記為守護(hù)線程或用戶線程。
void setPriority(int newPriority)更改線程的優(yōu)先級。
static void sleep(long millis)在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行),此操作受到系統(tǒng)計時器和調(diào)度程序精度和準(zhǔn)確性的影響。
static void sleep(long millis, int nanos)在指定的毫秒數(shù)加指定的納秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行),此操作受到系統(tǒng)計時器和調(diào)度程序精度和準(zhǔn)確性的影響。
void start() 使該線程開始執(zhí)行;Java 虛擬機(jī)調(diào)用該線程的 run 方法。
static void yield()暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程。
join
當(dāng)某個程序執(zhí)行流中調(diào)用其他線程的join()方法時,調(diào)用線程將被阻塞,直到被join()方法加入的join線程執(zhí)行完為止。
public class Join {
public static void main(String[] args) {
Thread thread = new JoinThread();
thread.start();
try {
//主線程等待thread的業(yè)務(wù)處理完了之后再向下運(yùn)行
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 0; i < 5; i++){
System.out.println(Thread.currentThread().getName()+" -- " + i);
}
}
}
class JoinThread extends Thread{
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println(Thread.currentThread().getName() + " -- "+i);
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//---------------運(yùn)行結(jié)果---------------------
//主線程等待JoinThread執(zhí)行完再執(zhí)行
Thread-0 -- 0
Thread-0 -- 1
Thread-0 -- 2
Thread-0 -- 3
Thread-0 -- 4
main -- 0
main -- 1
main -- 2
main -- 3
main -- 4
sleep
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態(tài)方法強(qiáng)制當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行),以“減慢線程”。
當(dāng)線程睡眠時,它睡在某個地方,在蘇醒之前不會返回到可運(yùn)行狀態(tài)。
當(dāng)睡眠時間到期,則返回到可運(yùn)行狀態(tài)。
線程睡眠的原因:線程執(zhí)行太快,或者需要強(qiáng)制進(jìn)入下一輪,因?yàn)镴ava規(guī)范不保證合理的輪換。
睡眠的實(shí)現(xiàn):調(diào)用靜態(tài)方法。
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
睡眠的位置:為了讓其他線程有機(jī)會執(zhí)行,可以將Thread.sleep()的調(diào)用放線程run()之內(nèi)。這樣才能保證該線程執(zhí)行過程中會睡眠。
注意:
- 線程睡眠是幫助所有線程獲得運(yùn)行機(jī)會的最好方法。
- 線程睡眠到期自動蘇醒,并返回到可運(yùn)行狀態(tài),不是運(yùn)行狀態(tài)。sleep()中指定的時間是線程不會運(yùn)行的最短時間。因此,sleep()方法不能保證該線程睡眠到期后就開始執(zhí)行。
- sleep()是靜態(tài)方法,只能控制當(dāng)前正在運(yùn)行的線程。
- sleep()在同步塊中執(zhí)行,不會釋放對象機(jī)鎖,其他對象無法訪問該對象直到sleep()方法執(zhí)行完并釋放機(jī)鎖。
public class Sleep {
public static void main(String[] args) {
new SleepThred().start();
}
}
class SleepThred extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if ((i) % 2 == 0) {
System.out.println("-------" + i);
}
System.out.print(i);
try {
Thread.sleep(1000);
System.out.print(" 線程睡眠1秒!\n");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//---------------運(yùn)行結(jié)果---------------------
//計數(shù)到10,在每個數(shù)字之間暫停1秒,每隔2個數(shù)字輸出一個字符串
-------0
0 線程睡眠1秒!
1 線程睡眠1秒!
-------2
2 線程睡眠1秒!
3 線程睡眠1秒!
-------4
4 線程睡眠1秒!
5 線程睡眠1秒!
-------6
6 線程睡眠1秒!
7 線程睡眠1秒!
-------8
8 線程睡眠1秒!
9 線程睡眠1秒!
sleep和wait的區(qū)別
Sleep
sleep()使當(dāng)前線程進(jìn)入停滯狀態(tài)(阻塞當(dāng)前線程),讓出CUP的使用權(quán),目的是不讓當(dāng)前線程獨(dú)自霸占該進(jìn)程所獲的CPU資源,以留一定時間給其他線程執(zhí)行的機(jī)會;
sleep()是Thread類的Static(靜態(tài))的方法,因此他不能改變對象的機(jī)鎖,所以當(dāng)在一個Synchronized塊中調(diào)用Sleep()方法是,線程雖然休眠了,但是對象的機(jī)鎖并木有被釋放,其他線程無法訪問這個對象(即使睡著也持有對象鎖)。
在sleep()休眠時間期滿后,該線程不一定會立即執(zhí)行,這是因?yàn)槠渌€程可能正在運(yùn)行而且沒有被調(diào)度為放棄執(zhí)行,除非此線程具有更高的優(yōu)先級。Wait
wait()方法是Object類里的方法,當(dāng)一個線程執(zhí)行到wait()方法時,它就進(jìn)入到一個和該對象相關(guān)的等待池中,同時失去(釋放)了對象的機(jī)鎖(暫時失去機(jī)鎖,wait(long timeout)超時時間到后還需要返還對象鎖),其他線程可以訪問。
wait()使用notify或者notifyAll或者指定睡眠時間來喚醒當(dāng)前等待池中的線程。
wiat()必須放在synchronized block中,否則會在program runtime時扔出 ”java.lang.IllegalMonitorStateException“ 異常。sleep()和wait()方法的最大區(qū)別是:
sleep()睡眠時,保持對象鎖,仍然占有該鎖
wait()睡眠時,釋放對象鎖。
wait()和sleep()都可以通過interrupt()方法打斷線程的暫停狀態(tài),從而使線程立刻拋出InterruptedException(但不建議使用該方法)。
public class MultiThread {
private static class Thread1 implements Runnable{
@Override
public void run() {
//由于 Thread1和下面Thread2內(nèi)部run方法要用同一對象作為監(jiān)視器,如果用this則Thread1和Threa2的this不是同一對象
//所以用MultiThread.class這個字節(jié)碼對象,當(dāng)前虛擬機(jī)里引用這個變量時指向的都是同一個對象
synchronized(MultiThread.class){
System.out.println("enter thread1 ...");
System.out.println("thread1 is waiting");
try{
//釋放鎖有兩種方式:(1)程序自然離開監(jiān)視器的范圍,即離開synchronized關(guān)鍵字管轄的代碼范圍
//(2)在synchronized關(guān)鍵字管轄的代碼內(nèi)部調(diào)用監(jiān)視器對象的wait()方法。這里使用wait方法
MultiThread.class.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread1 is going on ...");
System.out.println("thread1 is being over!");
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run() {
//notify方法并不釋放鎖,即使thread2調(diào)用了下面的sleep方法休息10ms,但thread1仍然不會執(zhí)行
//因?yàn)閠hread2沒有釋放鎖,所以Thread1得不到鎖而無法執(zhí)行
synchronized(MultiThread.class){
System.out.println("enter thread2 ...");
System.out.println("thread2 notify other thread can release wait status ...");
MultiThread.class.notify();
System.out.println("thread2 is sleeping ten millisecond ...");
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread2 is going on ...");
System.out.println("thread2 is being over!");
}
}
}
public static void main(String[] args) {
new Thread(new Thread1()).start();
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
new Thread(new Thread2()).start();
}
}
運(yùn)行結(jié)果
yield
yield()方法和sleep()方法有點(diǎn)相似,它也是Thread類提供的一個靜態(tài)方法,它也可以讓當(dāng)前正在執(zhí)行的線程暫停,但它不會阻塞該線程,它只是將該線程轉(zhuǎn)入到就緒狀態(tài)。即讓當(dāng)前線程暫停一下,讓系統(tǒng)的線程調(diào)度器重新調(diào)度一次,完全可能的情況是:當(dāng)某個線程調(diào)用了yield()方法暫停之后,線程調(diào)度器又將其調(diào)度出來重新執(zhí)行。
實(shí)際上,當(dāng)某個線程調(diào)用了yield()方法之后,只有優(yōu)先級與當(dāng)前線程相同或者比當(dāng)前線程更高的處于就緒狀態(tài)的線程才會獲得執(zhí)行機(jī)會。
sleep()與yield()方法區(qū)別
- sleep()方法暫停當(dāng)前線程后,會給其他線程執(zhí)行機(jī)會,不會理會其他線程的優(yōu)先級;但yield()方法只會給優(yōu)先級高或者相同的線程機(jī)會
- sleep()方法會將線程轉(zhuǎn)入到阻塞狀態(tài),直到經(jīng)過阻塞時間才會轉(zhuǎn)入就緒狀態(tài);而yield()不會將線程轉(zhuǎn)入阻塞狀態(tài),它只是強(qiáng)制當(dāng)前線程進(jìn)入就緒狀態(tài)。因此完全有可能某個線程調(diào)用了yield()方法暫停之后,立即再次獲取處理器資源被執(zhí)行。
- sleep()方法聲明拋出InterruptedException異常,所以調(diào)用sleep()方法時要么捕捉該異常,要么顯示聲明拋出該異常;而yield()方法則沒有聲明拋出任何異常。
- sleep()方法比yield()方法更好的可移植性,通常不建議使用yield()方法來控制并發(fā)線程執(zhí)行。
public class YieldExample
{
public static void main(String[] args)
{
Thread producer = new Producer();
Thread consumer = new Consumer();
producer.setPriority(Thread.MIN_PRIORITY); //Min Priority
consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority
producer.start();
consumer.start();
}
}
class Producer extends Thread
{
public void run()
{
for (int i = 0; i < 5; I++)
{
System.out.println("I am Producer : Produced Item " + i);
Thread.yield();
}
}
}
class Consumer extends Thread
{
public void run()
{
for (int i = 0; i < 5; I++)
{
System.out.println("I am Consumer : Consumed Item " + i);
Thread.yield();
}
}
}
//---------------運(yùn)行結(jié)果---------------------
//調(diào)用yield,兩個線程交替打印,依次把執(zhí)行機(jī)會交給對方
I am Producer : Produced Item 0
I am Consumer : Consumed Item 0
I am Producer : Produced Item 1
I am Consumer : Consumed Item 1
I am Producer : Produced Item 2
I am Consumer : Consumed Item 2
I am Producer : Produced Item 3
I am Consumer : Consumed Item 3
I am Producer : Produced Item 4
I am Consumer : Consumed Item 4
isAlive
判斷線程是否處于活動狀態(tài)
public class isAlive {
public static void main(String[] args) {
isAliveThread m = new isAliveThread();
Thread t = new Thread(m, "自定義線程");
System.out.println("線程執(zhí)行前:" + t.isAlive()); //false
t.start();
System.out.println("線程啟動之后:" + t.isAlive()); //true
}
}
class isAliveThread implements Runnable
{
public void run() {
System.out.println(Thread.currentThread().getName() + "運(yùn)行");
}
}
//---------------運(yùn)行結(jié)果---------------------
//線程啟動之后run方法體才會被執(zhí)行
線程執(zhí)行前:false
線程啟動之后:true
自定義線程運(yùn)行
線程優(yōu)先級
線程的優(yōu)先級用數(shù)字表示,范圍從1到10,默認(rèn)的是為5
每個線程默認(rèn)的優(yōu)先級與創(chuàng)建它的父線程的優(yōu)先級相同
優(yōu)先級越高的線程,被執(zhí)行的順序就比較靠前,在Thread中存在三個常量:
- MAX_PRIORITY
- MIN_PRIORITY
- NORM_PRIORITY
public class Priority {
public static void main(String[] args) {
Thread t1 = new Thread(new PriorityThread(), "線程A");
Thread t2 = new Thread(new PriorityThread(), "線程B");
Thread t3 = new Thread(new PriorityThread(), "線程C");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);
t3.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
class PriorityThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
//---------------運(yùn)行結(jié)果---------------------
線程A
線程C
線程B
//---------------運(yùn)行結(jié)果---------------------
線程B
線程A
線程C
//---------------運(yùn)行結(jié)果---------------------
線程A
線程B
線程C
看到此運(yùn)行結(jié)果,你一定很意外,小編也很意外,我以為會是ABC順序執(zhí)行,沒想到我多點(diǎn)幾次會出現(xiàn)3種情況,但是大多數(shù)情況下都是A,之后是B出現(xiàn)幾率比較大,C運(yùn)行在前面機(jī)會最小。因此即使是設(shè)置了優(yōu)先級,也不能保證該線程一定是最先執(zhí)行,只能說相對低優(yōu)先級的線程來說高優(yōu)先級的線程會優(yōu)先執(zhí)行。上述結(jié)果可能還會有其他的結(jié)果,小編猜測應(yīng)該是ABC自由組合的結(jié)果都有可能,讀者可以去嘗試一下。
Daemon
在Java程序中,只要前臺有一個線程在運(yùn)行,則整個Java進(jìn)程都不會消失,所以此時可以設(shè)置一個后臺線程,這樣即使Java進(jìn)程結(jié)束了,此后臺線程依然會執(zhí)行。要想實(shí)現(xiàn)這樣的操作,直接使用setDaemon()方法即可。
Java有兩種Thread:“守護(hù)線程Daemon”(守護(hù)線程)與“用戶線程User”(非守護(hù)線程)。
用戶線程:非守護(hù)線程包括常規(guī)的用戶線程或諸如用于處理GUI事件的事件調(diào)度線程,Java虛擬機(jī)在它所有非守護(hù)線程已經(jīng)離開后自動離開。
守護(hù)線程:守護(hù)線程則是用來服務(wù)用戶線程的,比如說GC線程。如果沒有其他用戶線程在運(yùn)行,那么就沒有可服務(wù)對象,也就沒有理由繼續(xù)下去。(操作系統(tǒng)里面是沒有所謂的守護(hù)線程的概念,只有守護(hù)進(jìn)程一說,但是Java語言機(jī)制是構(gòu)建在JVM的基礎(chǔ)之上的,意思是Java平臺把操作系統(tǒng)的底層給屏蔽起來,所以它可以在它自己的虛擬的平臺里面構(gòu)造出對 自己有利的機(jī)制,而語言或者說平臺的設(shè)計者多多少少是受到Unix思想的影響,而守護(hù)線程機(jī)制又是對JVM這樣的平臺湊合,于是守護(hù)線程應(yīng)運(yùn)而生)。
守護(hù)線程使用的情況較少,但并非無用,舉例來說,JVM的垃圾回收、內(nèi)存管理等線程都是守護(hù)線程。還有就是在做數(shù)據(jù)庫應(yīng)用時候,使用的數(shù)據(jù)庫連接池,連接池本身也包含著很多后臺線程,監(jiān)控連接個數(shù)、超時時間、狀態(tài)等等。
守護(hù)線程與用戶線程的唯一區(qū)別是:其實(shí)User Thread線程和Daemon Thread守護(hù)線程本質(zhì)上來說去沒啥區(qū)別的,唯一的區(qū)別之處就在虛擬機(jī)的離開,當(dāng)JVM中所有的線程都是守護(hù)線程的時候,JVM就可以退出了(如果User Thread全部撤離,那么Daemon Thread也就沒啥線程好服務(wù)的了,所以虛擬機(jī)也就退出了);如果還有一個或以上的非守護(hù)線程則不會退出。(以上是針對正常退出,調(diào)用System.exit則必定會退出)。
如何創(chuàng)建守護(hù)線程
守護(hù)線程與普通線程寫法上基本沒什么區(qū)別,調(diào)用線程對象的方法setDaemon(true),則可以將其設(shè)置為守護(hù)線程。
- thread.setDaemon(true)必須在thread.start()之前設(shè)置,你不能把正在運(yùn)行的常規(guī)線程設(shè)置為守護(hù)線程,否則會拋出IllegalThreadStateException異常,如果線程是守護(hù)線程,則isDaemon方法返回true。
- 在Daemon線程中產(chǎn)生的新線程也是Daemon的。
- 不是所有的應(yīng)用都可以分配給Daemon線程來進(jìn)行服務(wù),比如讀寫操作或者計算邏輯。因?yàn)樵贒aemon Thread還沒來的及進(jìn)行操作時,虛擬機(jī)可能已經(jīng)退出了。
public final void setDaemon(boolean on)
將該線程標(biāo)記為守護(hù)線程或用戶線程。當(dāng)正在運(yùn)行的線程都是守護(hù)線程時,Java 虛擬機(jī)退出。
該方法必須在啟動線程前調(diào)用。
該方法首先調(diào)用該線程的 checkAccess 方法,且不帶任何參數(shù)。這可能拋出 SecurityException。
參數(shù):
on - 如果為 true,則將該線程標(biāo)記為守護(hù)線程。
拋出:
IllegalThreadStateException - 如果該線程處于活動狀態(tài)。
SecurityException - 如果當(dāng)前線程無法修改該線程。
另請參見:
isDaemon(), checkAccess()
public class Daemon {
public static void main(String[] args) {
Thread t1 = new CommonThread();
Thread t2 = new Thread(new DaemonThread());
t2.setDaemon(true); // 設(shè)置為守護(hù)線程
t2.start();
t1.start();
}
}
class CommonThread extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" 用戶線程第 " + i + " 次執(zhí)行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class DaemonThread implements Runnable {
public void run() {
for (long i = 0; i < 9999999L; i++) {
System.out.println(Thread.currentThread().getName()+" 守護(hù)線程第 " + i + " 次執(zhí)行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//---------------運(yùn)行結(jié)果---------------------
//用戶線程退出,守護(hù)線程就沒有存在的意義了
Thread-0 用戶線程第 0 次執(zhí)行!
Thread-1 守護(hù)線程第 0 次執(zhí)行!
Thread-0 用戶線程第 1 次執(zhí)行!
Thread-1 守護(hù)線程第 1 次執(zhí)行!
Thread-0 用戶線程第 2 次執(zhí)行!
Thread-1 守護(hù)線程第 2 次執(zhí)行!
Thread-1 守護(hù)線程第 3 次執(zhí)行!
Thread-0 用戶線程第 3 次執(zhí)行!
Thread-1 守護(hù)線程第 4 次執(zhí)行!
Thread-0 用戶線程第 4 次執(zhí)行!
Thread-1 守護(hù)線程第 5 次執(zhí)行!
線程的中斷
一個線程可以被另一個線程中斷其操作的狀態(tài),使用interrupt()方法。
public class Interrupt {
public static void main(String[] args) {
InterruptThread m = new InterruptThread();
Thread t = new Thread(m, "自定義線程");
t.start();
try {
//sleep方法會出現(xiàn)異常
Thread.sleep(100);
} catch (InterruptedException e) {
}
// t.interrupt();
}
}
class InterruptThread implements Runnable{
@Override
public void run() {
try {
//sleep方法會出現(xiàn)異常
System.out.println("1、進(jìn)入run方法");
Thread.sleep(1000); //程序會暫停1秒再執(zhí)行
System.out.println("2、已經(jīng)完成了休眠");
} catch (InterruptedException e) {
System.out.println("3、休眠被終止!");
return; //返回方法調(diào)用處
}
System.out.println("4、run方法正常結(jié)束");
}
}
//---------------運(yùn)行結(jié)果---------------------
//正常情況下運(yùn)行結(jié)果 -- 不調(diào)用 interrupt() 方法
1、進(jìn)入run方法
2、已經(jīng)完成了休眠
4、run方法正常結(jié)束
//異常情況下運(yùn)行結(jié)果 -- 調(diào)用 interrupt() 方法
1、進(jìn)入run方法
3、休眠被終止!
終止線程
有三種方法可以使終止線程。
- 使用退出標(biāo)志,使線程正常退出,也就是當(dāng)run方法完成后線程終止。
- 使用stop()方法強(qiáng)行終止線程(這個方法不推薦使用,因?yàn)閟top和suspend、resume一樣,也可能發(fā)生不可預(yù)料的結(jié)果)。
- 使用interrupt方法中斷線程。
參考: