Java多線程的創(chuàng)建及啟動(dòng)
創(chuàng)新互聯(lián)建站是一家專注網(wǎng)站建設(shè)、網(wǎng)絡(luò)營(yíng)銷策劃、小程序定制開(kāi)發(fā)、電子商務(wù)建設(shè)、網(wǎng)絡(luò)推廣、移動(dòng)互聯(lián)開(kāi)發(fā)、研究、服務(wù)為一體的技術(shù)型公司。公司成立十年以來(lái),已經(jīng)為近千家成都廣告推廣各業(yè)的企業(yè)公司提供互聯(lián)網(wǎng)服務(wù)?,F(xiàn)在,服務(wù)的近千家客戶與我們一路同行,見(jiàn)證我們的成長(zhǎng);未來(lái),我們一起分享成功的喜悅。
Java中線程的創(chuàng)建常見(jiàn)有如三種基本形式
1.繼承Thread類,重寫該類的run()方法。
復(fù)制代碼
1 class MyThread extends Thread {
2 ?
3 ? ? private int i = 0;
4
5 ? ? @Override
6 ? ? public void run() {
7 ? ? ? ? for (i = 0; i 100; i++) {
8 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
9 ? ? ? ? }
10 ? ? }
11 }
復(fù)制代碼
復(fù)制代碼
1 public class ThreadTest {
2
3 ? ? public static void main(String[] args) {
4 ? ? ? ? for (int i = 0; i 100; i++) {
5 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
6 ? ? ? ? ? ? if (i == 30) {
7 ? ? ? ? ? ? ? ? Thread myThread1 = new MyThread(); ? ? // 創(chuàng)建一個(gè)新的線程 ?myThread1 ?此線程進(jìn)入新建狀態(tài)
8 ? ? ? ? ? ? ? ? Thread myThread2 = new MyThread(); ? ? // 創(chuàng)建一個(gè)新的線程 myThread2 此線程進(jìn)入新建狀態(tài)
9 ? ? ? ? ? ? ? ? myThread1.start(); ? ? ? ? ? ? ? ? ? ? // 調(diào)用start()方法使得線程進(jìn)入就緒狀態(tài)
10 ? ? ? ? ? ? ? ? myThread2.start(); ? ? ? ? ? ? ? ? ? ? // 調(diào)用start()方法使得線程進(jìn)入就緒狀態(tài)
11 ? ? ? ? ? ? }
12 ? ? ? ? }
13 ? ? }
14 }
復(fù)制代碼
如上所示,繼承Thread類,通過(guò)重寫run()方法定義了一個(gè)新的線程類MyThread,其中run()方法的方法體代表了線程需要完成的任務(wù),稱之為線程執(zhí)行體。當(dāng)創(chuàng)建此線程類對(duì)象時(shí)一個(gè)新的線程得以創(chuàng)建,并進(jìn)入到線程新建狀態(tài)。通過(guò)調(diào)用線程對(duì)象引用的start()方法,使得該線程進(jìn)入到就緒狀態(tài),此時(shí)此線程并不一定會(huì)馬上得以執(zhí)行,這取決于CPU調(diào)度時(shí)機(jī)。
2.實(shí)現(xiàn)Runnable接口,并重寫該接口的run()方法,該run()方法同樣是線程執(zhí)行體,創(chuàng)建Runnable實(shí)現(xiàn)類的實(shí)例,并以此實(shí)例作為Thread類的target來(lái)創(chuàng)建Thread對(duì)象,該Thread對(duì)象才是真正的線程對(duì)象。
復(fù)制代碼
1 class MyRunnable implements Runnable {
2 ? ? private int i = 0;
3
4 ? ? @Override
5 ? ? public void run() {
6 ? ? ? ? for (i = 0; i 100; i++) {
7 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
8 ? ? ? ? }
9 ? ? }
10 }
復(fù)制代碼
復(fù)制代碼
1 public class ThreadTest {
2
3 ? ? public static void main(String[] args) {
4 ? ? ? ? for (int i = 0; i 100; i++) {
5 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
6 ? ? ? ? ? ? if (i == 30) {
7 ? ? ? ? ? ? ? ? Runnable myRunnable = new MyRunnable(); // 創(chuàng)建一個(gè)Runnable實(shí)現(xiàn)類的對(duì)象
8 ? ? ? ? ? ? ? ? Thread thread1 = new Thread(myRunnable); // 將myRunnable作為Thread target創(chuàng)建新的線程
9 ? ? ? ? ? ? ? ? Thread thread2 = new Thread(myRunnable);
10 ? ? ? ? ? ? ? ? thread1.start(); // 調(diào)用start()方法使得線程進(jìn)入就緒狀態(tài)
11 ? ? ? ? ? ? ? ? thread2.start();
12 ? ? ? ? ? ? }
13 ? ? ? ? }
14 ? ? }
15 }
復(fù)制代碼
相信以上兩種創(chuàng)建新線程的方式大家都很熟悉了,那么Thread和Runnable之間到底是什么關(guān)系呢?我們首先來(lái)看一下下面這個(gè)例子。
復(fù)制代碼
1 public class ThreadTest {
2
3 ? ? public static void main(String[] args) {
4 ? ? ? ? for (int i = 0; i 100; i++) {
5 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
6 ? ? ? ? ? ? if (i == 30) {
7 ? ? ? ? ? ? ? ? Runnable myRunnable = new MyRunnable();
8 ? ? ? ? ? ? ? ? Thread thread = new MyThread(myRunnable);
9 ? ? ? ? ? ? ? ? thread.start();
10 ? ? ? ? ? ? }
11 ? ? ? ? }
12 ? ? }
13 }
14
15 class MyRunnable implements Runnable {
16 ? ? private int i = 0;
17
18 ? ? @Override
19 ? ? public void run() {
20 ? ? ? ? System.out.println("in MyRunnable run");
21 ? ? ? ? for (i = 0; i 100; i++) {
22 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
23 ? ? ? ? }
24 ? ? }
25 }
26
27 class MyThread extends Thread {
28
29 ? ? private int i = 0;
30 ?
31 ? ? public MyThread(Runnable runnable){
32 ? ? ? ? super(runnable);
33 ? ? }
34
35 ? ? @Override
36 ? ? public void run() {
37 ? ? ? ? System.out.println("in MyThread run");
38 ? ? ? ? for (i = 0; i 100; i++) {
39 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
40 ? ? ? ? }
41 ? ? }
42 }
復(fù)制代碼
同樣的,與實(shí)現(xiàn)Runnable接口創(chuàng)建線程方式相似,不同的地方在于
1 Thread thread = new MyThread(myRunnable);
那么這種方式可以順利創(chuàng)建出一個(gè)新的線程么?答案是肯定的。至于此時(shí)的線程執(zhí)行體到底是MyRunnable接口中的run()方法還是MyThread類中的run()方法呢?通過(guò)輸出我們知道線程執(zhí)行體是MyThread類中的run()方法。其實(shí)原因很簡(jiǎn)單,因?yàn)門hread類本身也是實(shí)現(xiàn)了Runnable接口,而run()方法最先是在Runnable接口中定義的方法。
1 public interface Runnable {
2 ?
3 ? ? public abstract void run();
4 ?
5 }
我們看一下Thread類中對(duì)Runnable接口中run()方法的實(shí)現(xiàn):
復(fù)制代碼
@Override
public void run() {
if (target != null) {
target.run();
}
}
復(fù)制代碼
也就是說(shuō),當(dāng)執(zhí)行到Thread類中的run()方法時(shí),會(huì)首先判斷target是否存在,存在則執(zhí)行target中的run()方法,也就是實(shí)現(xiàn)了Runnable接口并重寫了run()方法的類中的run()方法。但是上述給到的列子中,由于多態(tài)的存在,根本就沒(méi)有執(zhí)行到Thread類中的run()方法,而是直接先執(zhí)行了運(yùn)行時(shí)類型即MyThread類中的run()方法。
3.使用Callable和Future接口創(chuàng)建線程。具體是創(chuàng)建Callable接口的實(shí)現(xiàn)類,并實(shí)現(xiàn)clall()方法。并使用FutureTask類來(lái)包裝Callable實(shí)現(xiàn)類的對(duì)象,且以此FutureTask對(duì)象作為Thread對(duì)象的target來(lái)創(chuàng)建線程。
看著好像有點(diǎn)復(fù)雜,直接來(lái)看一個(gè)例子就清晰了。
復(fù)制代碼
1 public class ThreadTest {
2
3 ? ? public static void main(String[] args) {
4
5 ? ? ? ? CallableInteger myCallable = new MyCallable(); ? ?// 創(chuàng)建MyCallable對(duì)象
6 ? ? ? ? FutureTaskInteger ft = new FutureTaskInteger(myCallable); //使用FutureTask來(lái)包裝MyCallable對(duì)象
7
8 ? ? ? ? for (int i = 0; i 100; i++) {
9 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
10 ? ? ? ? ? ? if (i == 30) {
11 ? ? ? ? ? ? ? ? Thread thread = new Thread(ft); ? //FutureTask對(duì)象作為Thread對(duì)象的target創(chuàng)建新的線程
12 ? ? ? ? ? ? ? ? thread.start(); ? ? ? ? ? ? ? ? ? ? ?//線程進(jìn)入到就緒狀態(tài)
13 ? ? ? ? ? ? }
14 ? ? ? ? }
15
16 ? ? ? ? System.out.println("主線程for循環(huán)執(zhí)行完畢..");
17 ? ? ?
18 ? ? ? ? try {
19 ? ? ? ? ? ? int sum = ft.get(); ? ? ? ? ? ?//取得新創(chuàng)建的新線程中的call()方法返回的結(jié)果
20 ? ? ? ? ? ? System.out.println("sum = " + sum);
21 ? ? ? ? } catch (InterruptedException e) {
22 ? ? ? ? ? ? e.printStackTrace();
23 ? ? ? ? } catch (ExecutionException e) {
24 ? ? ? ? ? ? e.printStackTrace();
25 ? ? ? ? }
26
27 ? ? }
28 }
29
30
31 class MyCallable implements CallableInteger {
32 ? ? private int i = 0;
33
34 ? ? // 與run()方法不同的是,call()方法具有返回值
35 ? ? @Override
36 ? ? public Integer call() {
37 ? ? ? ? int sum = 0;
38 ? ? ? ? for (; i 100; i++) {
39 ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + " " + i);
40 ? ? ? ? ? ? sum += i;
41 ? ? ? ? }
42 ? ? ? ? return sum;
43 ? ? }
44
45 }
復(fù)制代碼
首先,我們發(fā)現(xiàn),在實(shí)現(xiàn)Callable接口中,此時(shí)不再是run()方法了,而是call()方法,此call()方法作為線程執(zhí)行體,同時(shí)還具有返回值!在創(chuàng)建新的線程時(shí),是通過(guò)FutureTask來(lái)包裝MyCallable對(duì)象,同時(shí)作為了Thread對(duì)象的target。那么看下FutureTask類的定義:
1 public class FutureTaskV implements RunnableFutureV {
2 ?
3 ? ? //....
4 ?
5 }
1 public interface RunnableFutureV extends Runnable, FutureV {
2 ?
3 ? ? void run();
4 ?
5 }
于是,我們發(fā)現(xiàn)FutureTask類實(shí)際上是同時(shí)實(shí)現(xiàn)了Runnable和Future接口,由此才使得其具有Future和Runnable雙重特性。通過(guò)Runnable特性,可以作為Thread對(duì)象的target,而Future特性,使得其可以取得新創(chuàng)建線程中的call()方法的返回值。
執(zhí)行下此程序,我們發(fā)現(xiàn)sum = 4950永遠(yuǎn)都是最后輸出的。而“主線程for循環(huán)執(zhí)行完畢..”則很可能是在子線程循環(huán)中間輸出。由CPU的線程調(diào)度機(jī)制,我們知道,“主線程for循環(huán)執(zhí)行完畢..”的輸出時(shí)機(jī)是沒(méi)有任何問(wèn)題的,那么為什么sum =4950會(huì)永遠(yuǎn)最后輸出呢?
原因在于通過(guò)ft.get()方法獲取子線程call()方法的返回值時(shí),當(dāng)子線程此方法還未執(zhí)行完畢,ft.get()方法會(huì)一直阻塞,直到call()方法執(zhí)行完畢才能取到返回值。
上述主要講解了三種常見(jiàn)的線程創(chuàng)建方式,對(duì)于線程的啟動(dòng)而言,都是調(diào)用線程對(duì)象的start()方法,需要特別注意的是:不能對(duì)同一線程對(duì)象兩次調(diào)用start()方法。
你好,本題已解答,如果滿意
請(qǐng)點(diǎn)右下角“采納答案”。
package threadgroup;
class ThreadDemo3 extends Thread {
private String name;
private int delay;
public ThreadDemo3(String sname, int i_delay) {
name = sname;
delay = i_delay;
}
public void run() {
try {
sleep(delay);
} catch (InterruptedException e) {
}
System.out.println("多線程測(cè)試!\n" + name + "\n" + delay);
}
}
public class testMyThread {
public static void main(String[] args) {
ThreadDemo3 th1,th2,th3;
th1 = new ThreadDemo3("線程1", (int) (Math.random() * 900));
th2 = new ThreadDemo3("線程2", (int) (Math.random() * 900));
th3 = new ThreadDemo3("線程3", (int) (Math.random() * 900));
th1.start();
th2.start();
th3.start();
}
}
package threadgroup;
public class threadDemo {
public static void main(String[] args) {
Thread t = Thread.currentThread();
t.setName("你好嗎?");
System.out.println("正在進(jìn)行的Thread是:" + t);
try {
for (int i = 0; i 5; i++) {
System.out.println("我不叫穆繼超" + i);
Thread.sleep(3000);
}
} catch (Exception e) {
// TODO: handle exception
System.out.println("Thread has wrong" + e.getMessage());
}
}
}
package threadgroup;
public class threadDemo2 implements Runnable {
public threadDemo2() {
Thread t1 = Thread.currentThread();
t1.setName("第一個(gè)主進(jìn)程");
System.out.println("正在運(yùn)行" + t1);
Thread t2 = new Thread(this, "");
System.out.println("在創(chuàng)建一個(gè)進(jìn)程");
t2.start();
try {
System.out.println("使他進(jìn)入第一個(gè)睡眠狀態(tài)");
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Thread has wrong" + e.getMessage());
}
System.out.println("退出第一個(gè)進(jìn)程");
}
public void run() {
try {
for (int i = 0; i 5; i++) {
System.out.println("進(jìn)程" + i);
Thread.sleep(3000);
}
} catch (InterruptedException e) {
// TODO: handle exception
System.out.println("Thread has wrong" + e.getMessage());
}
System.out.println("退出第二個(gè)進(jìn)程");
}
public static void main(String[] args) {
new threadDemo2();
}
}
多進(jìn)程是指操作系統(tǒng)能同時(shí)運(yùn)行多個(gè)任務(wù)(程序),多線程是指在同一程序中有多個(gè)順序流在執(zhí)行。
在java中創(chuàng)建一個(gè)線程有兩種方法:
package?com.thread;
public?class?ThreadTest1?{
public?static?void?main(String[]?args)?{
Runnable1?r?=?new?Runnable1();
//r.run();并不是線程開(kāi)啟,而是簡(jiǎn)單的方法調(diào)用
Thread?t?=?new?Thread(r);//創(chuàng)建線程
//t.run();?//如果該線程是使用獨(dú)立的?Runnable?運(yùn)行對(duì)象構(gòu)造的,則調(diào)用該?Runnable?對(duì)象的?run?方法;否則,該方法不執(zhí)行任何操作并返回。
t.start();?//線程開(kāi)啟
for?(int?i?=?0;?i??100;?i++)?{
System.out.println("main:"+i);
}
}
}
class?Runnable1?implements?Runnable{
public?void?run()?{
for?(int?i?=?0;?i??100;?i++)?{
System.out.println("Thread-----:"+i);
}
}
}
因?yàn)槟鉵ew了兩次
試著在Task類內(nèi)創(chuàng)建一個(gè)對(duì)象 然后鎖住這個(gè)對(duì)象
1、java多線程調(diào)用同步方法時(shí)主程序或者后臺(tái)服務(wù)Service程序兩者必須有一個(gè)正在運(yùn)行著,定時(shí)任務(wù)才會(huì)執(zhí)行。
2、自動(dòng)執(zhí)行的任務(wù)可以設(shè)置開(kāi)啟或關(guān)閉定時(shí),檢查任務(wù)的這一開(kāi)關(guān)處于開(kāi)啟狀態(tài)。
3、源目錄在定時(shí)的時(shí)間點(diǎn)如果沒(méi)有變化,任務(wù)執(zhí)行后不會(huì)留下日志,通過(guò)查看任務(wù)的最后執(zhí)行時(shí)間確定任務(wù)到底執(zhí)行了沒(méi)有。
4、執(zhí)行失敗和沒(méi)有執(zhí)行不是同一回事,如果是執(zhí)行失敗則要分析失敗原因。這是java多線程調(diào)用同步方法時(shí),休眠后代碼執(zhí)行不到的原因。
線程用到Thread或者Runnable接口(Thread也操作了Runnable接口)
繼承了Thread類后需要重載其run方法,在方法里寫你需要完成的事情,開(kāi)始線程是調(diào)用其start方法。
操作Runnable接口必須實(shí)現(xiàn)其run方法,在方法里寫你需要完成的事情,Runnable接口沒(méi)有start方法,所以啟動(dòng)線程還是需要依靠Thread類 new Thread(Runnable runnable).start();
一般項(xiàng)目中多是操作接口,因?yàn)轭愔荒軉卫^承,接口可以操作多個(gè)。