java中thread实例_Java多线程2:Thread中的实例方法
Thread類中的方法調(diào)用方式:
學(xué)習(xí)Thread類中的方法是學(xué)習(xí)多線程的第一步。在學(xué)習(xí)多線程之前特別提出一點(diǎn),調(diào)用Thread中的方法的時(shí)候,在線程類中,有兩種方式,一定要理解這兩種方式的區(qū)別:
1、this.XXX()
這種調(diào)用方式表示的線程是線程實(shí)例本身
2、Thread.currentThread.XXX()或Thread.XXX()
上面兩種寫法是一樣的意思。這種調(diào)用方式表示的線程是正在執(zhí)行Thread.currentThread.XXX()所在代碼塊的線程
當(dāng)然,這么說,肯定有人不理解兩者之間的差別。沒有關(guān)系,之后會講清楚,尤其是在講Thread構(gòu)造函數(shù)這塊。講解后,再回過頭來看上面2點(diǎn),會加深理解。
Thread類中的實(shí)例方法
從Thread類中的實(shí)例方法和類方法的角度講解Thread中的方法,這種區(qū)分的角度也有助于理解多線程中的方法。實(shí)例方法,只和實(shí)例線程(也就是new出來的線程)本身掛鉤,和當(dāng)前運(yùn)行的是哪個(gè)線程無關(guān)??聪耇hread類中的實(shí)例方法:
1、start()
start()方法的作用講得直白點(diǎn)就是通知"線程規(guī)劃器",此線程可以運(yùn)行了,正在等待CPU調(diào)用線程對象得run()方法,產(chǎn)生一個(gè)異步執(zhí)行的效果。通過start()方法產(chǎn)生得到結(jié)論,先看下代碼:
public class MyThread02 extends Thread
{
public void run()
{
try
{
for (int i = 0; i < 3; i++)
{
Thread.sleep((int)(Math.random() * 1000));
System.out.println("run = " + Thread.currentThread().getName());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
MyThread02 mt = new MyThread02();
mt.start();
try
{
for (int i = 0; i < 3; i++)
{
Thread.sleep((int)(Math.random() * 1000));
System.out.println("run = " + Thread.currentThread().getName());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
看下運(yùn)行結(jié)果:
run = Thread-0
run = main
run = main
run = main
run = Thread-0
run = Thread-0
結(jié)果表明了:CPU執(zhí)行哪個(gè)線程的代碼具有不確定性。再看另外一個(gè)例子:
public class MyThread03 extends Thread
{
public void run()
{
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args)
{
MyThread03 mt0 = new MyThread03();
MyThread03 mt1 = new MyThread03();
MyThread03 mt2 = new MyThread03();
mt0.start();
mt1.start();
mt2.start();
}
看下運(yùn)行結(jié)果:
Thread-1
Thread-2
Thread-0
盡管啟動線程是按照mt0、mt1、mt2,但是實(shí)際的啟動順序卻是Thread-1、Thread-2、Thread-0。這個(gè)例子說明了:調(diào)用start()方法的順序不代表線程啟動的順序,線程啟動順序具有不確定性。
2、run()
線程開始執(zhí)行,虛擬機(jī)調(diào)用的是線程run()方法中的內(nèi)容。稍微改一下之前的例子看一下:
public static void main(String[] args)
{
MyThread02 mt = new MyThread02();
mt.run();
try
{
for (int i = 0; i < 3; i++)
{
Thread.sleep((int)(Math.random() * 1000));
System.out.println("run = " + Thread.currentThread().getName());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
MyThread02的代碼不變,看下運(yùn)行結(jié)果:
run = main
run = main
run = main
run = main
run = main
run = main
看到打印了6次的"run = main",說明如果只有run()沒有start(),Thread實(shí)例run()方法里面的內(nèi)容是沒有任何異步效果的,全部被main函數(shù)執(zhí)行。換句話說,只有run()而不調(diào)用start()啟動線程是沒有任何意義的。
3、isAlive()
測試線程是否處于活動狀態(tài),只要線程啟動且沒有終止,方法返回的就是true。看一下例子:
public class MyThread06 extends Thread
{
public void run()
{
System.out.println("run = " + this.isAlive());
}
}
public static void main(String[] args) throws Exception
{
MyThread06 mt = new MyThread06();
System.out.println("begin == " + mt.isAlive());
mt.start();
Thread.sleep(100);
System.out.println("end == " + mt.isAlive());
}
看下運(yùn)行結(jié)果:
begin == false
run = true
end == false
看到在start()之前,線程的isAlive是false,start()之后就是true了。main函數(shù)中加上Thread.sleep(100)的原因是為了確保Thread06的run()方法中的代碼執(zhí)行完,否則有可能end這里打印出來的是true,有興趣可以自己試驗(yàn)一下。
4、getId()
這個(gè)方法比較簡單,就不寫例子了。在一個(gè)Java應(yīng)用中,有一個(gè)long型的全局唯一的線程ID生成器threadSeqNumber,每new出來一個(gè)線程都會把這個(gè)自增一次,并賦予線程的tid屬性,這個(gè)是Thread自己做的,用戶無法執(zhí)行一個(gè)線程的Id。
5、getName()
這個(gè)方法也比較簡單,也不寫例子了。我們new一個(gè)線程的時(shí)候,可以指定該線程的名字,也可以不指定。如果指定,那么線程的名字就是我們自己指定的,getName()返回的也是開發(fā)者指定的線程的名字;如果不指定,那么Thread中有一個(gè)int型全局唯一的線程初始號生成器threadInitNum,Java先把threadInitNum自增,然后以"Thread-threadInitNum"的方式來命名新生成的線程
6、getPriority()和setPriority(int newPriority)
這兩個(gè)方法用于獲取和設(shè)置線程的優(yōu)先級,優(yōu)先級高的CPU得到的CPU資源比較多,設(shè)置優(yōu)先級有助于幫"線程規(guī)劃器"確定下一次選擇哪一個(gè)線程優(yōu)先執(zhí)行。換句話說,兩個(gè)在等待CPU的線程,優(yōu)先級高的線程越容易被CU選擇執(zhí)行。下面來看一下例子,并得出幾個(gè)結(jié)論:
public class MyThread09_0 extends Thread
{
public void run()
{
System.out.println("MyThread9_0 run priority = " +
this.getPriority());
}
}
public class MyThread09_1 extends Thread
{
public void run()
{
System.out.println("MyThread9_1 run priority = " +
this.getPriority());
MyThread09_0 thread = new MyThread09_0();
thread.start();
}
}
public static void main(String[] args)
{
System.out.println("main thread begin, priority = " +
Thread.currentThread().getPriority());
System.out.println("main thread end, priority = " +
Thread.currentThread().getPriority());
MyThread09_1 thread = new MyThread09_1();
thread.start();
}
看一下運(yùn)行結(jié)果:
main thread begin, priority = 5
main thread end, priority = 5
MyThread9_1 run priority = 5
MyThread9_0 run priority = 5
從這個(gè)例子我們得出結(jié)論:線程默認(rèn)優(yōu)先級為5,如果不手動指定,那么線程優(yōu)先級具有繼承性,比如線程A啟動線程B,那么線程B的優(yōu)先級和線程A的優(yōu)先級相同。下面的例子演示了設(shè)置線程優(yōu)先級帶來的效果:
public class MyThread10_0 extends Thread
{
public void run()
{
long beginTime = System.currentTimeMillis();
for (int j = 0; j < 100000; j++){}
long endTime = System.currentTimeMillis();
System.out.println("◆◆◆◆◆ thread0 use time = " +
(endTime - beginTime));
}
}
public class MyThread10_1 extends Thread
{
public void run()
{
long beginTime = System.currentTimeMillis();
for (int j = 0; j < 100000; j++){}
long endTime = System.currentTimeMillis();
System.out.println("◇◇◇◇◇ thread1 use time = " +
(endTime - beginTime));
}
}
public static void main(String[] args)
{
for (int i = 0; i < 5; i++)
{
MyThread10_0 mt0 = new MyThread10_0();
mt0.setPriority(5);
mt0.start();
MyThread10_1 mt1 = new MyThread10_1();
mt1.setPriority(4);
mt1.start();
}
}
看下運(yùn)行結(jié)果:
◆◆◆◆◆ thread0 use time = 0
◆◆◆◆◆ thread0 use time = 0
◆◆◆◆◆ thread0 use time = 1
◆◆◆◆◆ thread0 use time = 2
◆◆◆◆◆ thread0 use time = 2
◇◇◇◇◇ thread1 use time = 0
◇◇◇◇◇ thread1 use time = 1
◇◇◇◇◇ thread1 use time = 5
◇◇◇◇◇ thread1 use time = 2
◇◇◇◇◇ thread1 use time = 0
看到黑色菱形(線程優(yōu)先級高的)先打印完,再多試幾次也是一樣的。為了產(chǎn)生一個(gè)對比效果,把yMyThread10_0的優(yōu)先級設(shè)置為4,看下運(yùn)行結(jié)果:
◆◆◆◆◆ thread0 use time = 0
◇◇◇◇◇ thread1 use time = 1
◇◇◇◇◇ thread1 use time = 1
◆◆◆◆◆ thread0 use time = 0
◇◇◇◇◇ thread1 use time = 0
◆◆◆◆◆ thread0 use time = 1
◆◆◆◆◆ thread0 use time = 1
◇◇◇◇◇ thread1 use time = 2
◇◇◇◇◇ thread1 use time = 1
◆◆◆◆◆ thread0 use time = 0
看到,馬上差別就出來了。從這個(gè)例子我們得出結(jié)論:CPU會盡量將執(zhí)行資源讓給優(yōu)先級比較高的線程。
7、isDaeMon、setDaemon(boolean on)
講解兩個(gè)方法前,首先要知道理解一個(gè)概念。Java中有兩種線程,一種是用戶線程,一種是守護(hù)線程。守護(hù)線程是一種特殊的線程,它的作用是為其他線程的運(yùn)行提供便利的服務(wù),最典型的應(yīng)用便是GC線程。如果進(jìn)程中不存在非守護(hù)線程了,那么守護(hù)線程自動銷毀,因?yàn)闆]有存在的必要,為別人服務(wù),結(jié)果服務(wù)的對象都沒了,當(dāng)然就銷毀了。理解了這個(gè)概念后,看一下例子:
public class MyThread11 extends Thread
{
private int i = 0;
public void run()
{
try
{
while (true)
{
i++;
System.out.println("i = " + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
try
{
MyThread11 mt = new MyThread11();
mt.setDaemon(true);
mt.start();
Thread.sleep(5000);
System.out.println("我離開thread對象再也不打印了,我停止了!");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
看一下運(yùn)行結(jié)果:
1 i = 1
2 i = 2
3 i = 3
4 i = 4
5 i = 5
6 我離開thread對象再也不打印了,我停止了!
要解釋一下。我們將MyThread11線程設(shè)置為守護(hù)線程,看到第6行的那句話,而i停在6不會再運(yùn)行了。這說明,main線程運(yùn)行了5秒多結(jié)束,而i每隔1秒累加一次,5秒后main線程執(zhí)行完結(jié)束了,MyThread11作為守護(hù)線程,main函數(shù)都運(yùn)行完了,自然也沒有存在的必要了,就自動銷毀了,因此也就沒有再往下打印數(shù)字。
關(guān)于守護(hù)線程,有一個(gè)細(xì)節(jié)注意下,setDaemon(true)必須在線程start()之前
8、interrupt()
這是一個(gè)有點(diǎn)誤導(dǎo)性的名字,實(shí)際上Thread類的interrupt()方法無法中斷線程。看一下例子:
public class MyThread12 extends Thread
{
public void run()
{
for (int i = 0; i < 500000; i++)
{
System.out.println("i = " + (i + 1));
}
}
}
public static void main(String[] args)
{
try
{
MyThread12 mt = new MyThread12();
mt.start();
Thread.sleep(2000);
mt.interrupt();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
看下運(yùn)行結(jié)果:
...
i = 499995
i = 499996
i = 499997
i = 499998
i = 499999
i = 500000
看結(jié)果還是打印到了50000。也就是說,盡管調(diào)用了interrupt()方法,但是線程并沒有停止。interrupt()方法的作用實(shí)際上是:在線程受到阻塞時(shí)拋出一個(gè)中斷信號,這樣線程就得以退出阻塞狀態(tài)。換句話說,沒有被阻塞的線程,調(diào)用interrupt()方法是不起作用的。關(guān)于這個(gè)會在之后講中斷機(jī)制的時(shí)候,專門寫一篇文章講解。
9、isInterrupted()
測試線程是否已經(jīng)中斷,但不清除狀態(tài)標(biāo)識。這個(gè)和interrupt()方法一樣,在后面講中斷機(jī)制的文章中專門會講到。
10、join()
join()方法的作用是等待線程銷毀。join()方法反應(yīng)的是一個(gè)很現(xiàn)實(shí)的問題,比如main線程的執(zhí)行時(shí)間是1s,子線程的執(zhí)行時(shí)間是10s,但是主線程依賴子線程執(zhí)行完的結(jié)果,這時(shí)怎么辦?可以像生產(chǎn)者/消費(fèi)者模型一樣,搞一個(gè)緩沖區(qū),子線程執(zhí)行完把數(shù)據(jù)放在緩沖區(qū)中,通知main線程,main線程去拿,這樣就不會浪費(fèi)main線程的時(shí)間了。另外一種方法,就是join()了。先看一下例子:
public class MyThread36 extends Thread
{
public void run()
{
try
{
int secondValue = (int)(Math.random() * 10000);
System.out.println(secondValue);
Thread.sleep(secondValue);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception
{
MyThread36 mt = new MyThread36();
mt.start();
mt.join();
System.out.println("我想當(dāng)mt對象執(zhí)行完畢之后我再執(zhí)行,我做到了");
}
看一下運(yùn)行結(jié)果:
3111
我想當(dāng)mt對象執(zhí)行完畢之后我再執(zhí)行,我做到了
意思是,join()方法會使調(diào)用join()方法的線程(也就是mt線程)所在的線程(也就是main線程)無限阻塞,直到調(diào)用join()方法的線程銷毀為止,此例中main線程就會無限期阻塞直到mt的run()方法執(zhí)行完畢。
join()方法的一個(gè)重點(diǎn)是要區(qū)分出和sleep()方法的區(qū)別。join(2000)也是可以的,表示調(diào)用join()方法所在的線程最多等待2000ms,兩者的區(qū)別在于:
sleep(2000)不釋放鎖,join(2000)釋放鎖,因?yàn)閖oin()方法內(nèi)部使用的是wait(),因此會釋放鎖。看一下join(2000)的源碼就知道了,join()其實(shí)和join(2000)一樣,無非是join(0)而已:
1 public final synchronized void join(long millis)
2 throws InterruptedException {
3 long base = System.currentTimeMillis();
4 long now = 0;
5
6 if (millis < 0) {
7 throw new IllegalArgumentException("timeout value is negative");
8 }
9
10 if (millis == 0) {
11 while (isAlive()) {
12 wait(0);
13 }
14 } else {
15 while (isAlive()) {
16 long delay = millis - now;
17 if (delay <= 0) {
18 break;
19 }
20 wait(delay);
21 now = System.currentTimeMillis() - base;
22 }
23 }
24 }
第12行、第20行應(yīng)該已經(jīng)很清楚了。
總結(jié)
以上是生活随笔為你收集整理的java中thread实例_Java多线程2:Thread中的实例方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java不满足的依赖异常_不要依赖代码中
- 下一篇: java函數_函數(Java版)