Java使用java.lang.Thread
類代表線程,所有的線程對象都必須是Thread類或其子類的實(shí)例。每個線程的作用是完成一定的任務(wù),實(shí)際上就是執(zhí)行一段程序流即一段順序執(zhí)行的代碼。Java使用線程執(zhí)行體來代表這段程序流。Java中通過繼承Thread類來創(chuàng)建并啟動多線程的步驟如下:
成都創(chuàng)新互聯(lián)公司專注于金州企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站設(shè)計,成都做商城網(wǎng)站。金州網(wǎng)站建設(shè)公司,為金州等地區(qū)提供建站服務(wù)。全流程定制設(shè)計,專業(yè)設(shè)計,全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
代碼如下:
自定義線程類
/**
* @author bruceliu
* @create 2019-05-29 23:15
* @description 繼承Thread,重寫父類的run()方法
*/
public class MyThread extends Thread {
/*
* 利用繼承中的特點(diǎn)
* 將線程名稱傳遞 進(jìn)行設(shè)置
*/
public MyThread(String name) {
super(name);
}
/*
* 重寫run方法
* 定義線程要執(zhí)行的代碼
*/
public void run() {
for (int i = 0; i < 20; i++) {
//getName()方法 來自父親
System.out.println(getName() + i);
}
}
}
測試類
/**
* @author bruceliu
* @create 2019-05-29 23:17
* @description 繼承Thread,重寫父類的run()方法
*/
public class TestMyThread {
public static void main(String[] args) {
System.out.println("這里是main線程");
MyThread mt = new MyThread("小強(qiáng)");
mt.start();//開啟了一個新的線程
for (int i = 0; i < 20; i++) {
System.out.println("旺財:"+i);
}
}
}
有可能有些人看不到這么明顯的效果,這也很正常。所謂的多線程,指的是兩個線程的代碼可以同時運(yùn)行,而不必一個線程需要等待另一個線程內(nèi)的代碼執(zhí)行完才可以運(yùn)行。對于單核CPU來說,是無法做到真正的多線程的,每個時間點(diǎn)上,CPU都會執(zhí)行特定的代碼,由于CPU執(zhí)行代碼時間很快,所以兩個線程的代碼交替執(zhí)行看起來像是同時執(zhí)行的一樣。那具體執(zhí)行某段代碼多少時間,就和分時機(jī)制系統(tǒng)有關(guān)了。分時系統(tǒng)把CPU時間劃分為多個時間片,操作系統(tǒng)以時間片為單位片為單位各個線程的代碼,越好的CPU分出的時間片越小。所以看不到明顯效果也很正常,一個線程打印5句話本來就很快,可能在分出的時間片內(nèi)就執(zhí)行完成了。所以,最簡單的解決辦法就是把for循環(huán)的值調(diào)大一點(diǎn)就可以了(也可以在for循環(huán)里加Thread.sleep方法,這個之后再說)。
采用 java.lang.Runnable 也是非常常見的一種,我們只需要重寫run方法即可。和繼承自Thread類差不多,不過實(shí)現(xiàn)Runnable后,還是要通過一個Thread來啟動:
代碼如下:
/**
* @author bruceliu
* @create 2019-05-30 11:37
* @description 實(shí)現(xiàn)Runnable接口,重寫run方法
*/
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
線程測試類
/**
* @author bruceliu
* @create 2019-05-30 11:39
* @description 實(shí)現(xiàn)Runnable接口,重寫run方法
*/
public class TestMyRunnable {
public static void main(String[] args) {
//創(chuàng)建自定義類對象 線程任務(wù)對象
MyRunnable mr = new MyRunnable();
//創(chuàng)建線程對象
Thread t = new Thread(mr, "小強(qiáng)");
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("旺財 " + i);
}
}
}
通過實(shí)現(xiàn)Runnable接口,使得該類有了多線程類的特征。run()方法是多線程程序的一個執(zhí)行目標(biāo)。所有的多線程代碼都在run方法里面。Thread類實(shí)際上也是實(shí)現(xiàn)了Runnable接口的類。
在啟動的多線程的時候,需要先通過Thread類的構(gòu)造方法Thread(Runnable target) 構(gòu)造出對象,然后調(diào)用Thread對象的start()方法來運(yùn)行多線程代碼。
實(shí)際上所有的多線程代碼都是通過運(yùn)行Thread的start()方法來運(yùn)行的。因此,不管是繼承Thread類還是實(shí)現(xiàn)Runnable接口來實(shí)現(xiàn)多線程,最終還是通過Thread的對象的API來控制線程的,熟悉Thread類的API是進(jìn)行多線程編程的基礎(chǔ)。
Runnable對象僅僅作為Thread對象的target,Runnable實(shí)現(xiàn)類里包含的run()方法僅作為線程執(zhí)行體。而實(shí)際的線程對象依然是Thread實(shí)例,只是該Thread線程負(fù)責(zé)執(zhí)行其target的run()方法。
使用線程的內(nèi)匿名內(nèi)部類方式,可以方便的實(shí)現(xiàn)每個線程執(zhí)行不同的線程任務(wù)操作。
使用匿名內(nèi)部類的方式實(shí)現(xiàn)Runnable接口,重新Runnable接口中的run方法:
代碼如下:
/**
* @author bruceliu
* @create 2019-05-30 11:55
* @description 使用匿名內(nèi)部類方式
*/
public class NoNameInnerClassThread {
public static void main(String[] args) {
System.out.println("-----多線程創(chuàng)建開始-----");
//‐‐‐這個整體 相當(dāng)于new MyRunnable()
Runnable r = new Runnable(){
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("張宇:"+i);
}
}
};
new Thread(r).start();
for (int i = 0; i < 20; i++) {
System.out.println("費(fèi)玉清:"+i);
}
System.out.println("-----多線程創(chuàng)建結(jié)束-----");
}
}
如果一個類繼承Thread,則不適合資源共享。但是如果實(shí)現(xiàn)了Runable接口的話,則很容易的實(shí)現(xiàn)資源共享。
總結(jié):
在java中,每次程序運(yùn)行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因?yàn)槊慨?dāng)使用
java命令執(zhí)行一個類的時候,實(shí)際上都會啟動一個JVM,每一個JVM其實(shí)在就是在操作系統(tǒng)中啟動了一個進(jìn)程。
我們已經(jīng)可以完成最基本的線程開啟,那么在我們完成操作過程中用到了 java.lang.Thread 類,
API中該類中定義了有關(guān)線程的一些方法,具體如下:
public Thread()
:分配一個新的線程對象。public Thread(String name)
:分配一個指定名字的新的線程對象。public Thread(Runnable target)
:分配一個帶有指定目標(biāo)新的線程對象。public Thread(Runnable target,String name)
:分配一個帶有指定目標(biāo)新的線程對象并指定名字。public String getName()
:獲取當(dāng)前線程名稱。public void start()
:導(dǎo)致此線程開始執(zhí)行; Java虛擬機(jī)調(diào)用此線程的run方法。public void run()
:此線程要執(zhí)行的任務(wù)在此處定義代碼。public static void sleep(long millis)
:使當(dāng)前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時停止執(zhí)行)。public static Thread currentThread()
:返回對當(dāng)前正在執(zhí)行的線程對象的引用
在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護(hù)線程)
用個比較通俗的比如,任何一個守護(hù)線程都是整個JVM中所有非守護(hù)線程的保姆:只要當(dāng)前JVM實(shí)例中尚存在任何一個非守護(hù)線程沒有結(jié)束,守護(hù)線程就全部工作;只有當(dāng)最后一個非守護(hù)線程結(jié)束時,守護(hù)線程隨著JVM一同結(jié)束工作。Daemon的作用是為其他線程的運(yùn)行提供便利服務(wù),守護(hù)線程最典型的應(yīng)用就是 GC (垃圾回收器),它就是一個很稱職的守護(hù)者。
User和Daemon兩者幾乎沒有區(qū)別,唯一的不同之處就在于虛擬機(jī)的離開:如果 User Thread已經(jīng)全部退出運(yùn)行了,只剩下Daemon Thread存在了,虛擬機(jī)也就退出了。 因?yàn)闆]有了被守護(hù)者,Daemon也就沒有工作可做了,也就沒有繼續(xù)運(yùn)行程序的必要了。
首先看下哪些是守護(hù)線程,哪些是非守護(hù)線程
值得一提的是,守護(hù)線程并非只有虛擬機(jī)內(nèi)部提供,用戶在編寫程序時也可以自己設(shè)置守護(hù)線程。下面的方法就是用來設(shè)置守護(hù)線程的。
Thread daemonTread = new Thread();
// 設(shè)定 daemonThread 為 守護(hù)線程,default false(非守護(hù)線程)
daemonThread.setDaemon(true);
// 驗(yàn)證當(dāng)前線程是否為守護(hù)線程,返回 true 則為守護(hù)線程
daemonThread.isDaemon();
代碼示例:
/**
* @author bruceliu
* @create 2019-06-01 17:46
* @description 守護(hù)線程:進(jìn)程線程(主線程掛了) 守護(hù)線程也會被自動銷毀.
*/
public class DaemonThread {
public static void main(String[] args) {
System.out.println("----------->主線程執(zhí)行開始......");
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
System.out.println("我是子線程......");
}
}
});
//設(shè)置為守護(hù)線程
thread.setDaemon(true);
thread.start();
for (int i = 0; i <10 ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
System.out.println("我是主線程...");
}
System.out.println("----------->主線程執(zhí)行完畢......");
}
}