概要
線程作為獨(dú)立調(diào)度運(yùn)行和分派的基本單位银室,很明顯的一個(gè)特征便是可運(yùn)行的偏化。Runnable接口便是對(duì)可被調(diào)度運(yùn)行進(jìn)行了抽象聲明盈蛮。本章主要內(nèi)容如下:
- 線程構(gòu)造函數(shù)介紹
- Thread和Runnable的區(qū)別與聯(lián)系
- 創(chuàng)建線程示例
線程構(gòu)造函數(shù)介紹
/**
* 無(wú)參構(gòu)造函數(shù)娘侍,常用
*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
/**
* 需要參數(shù)Runnable對(duì)象構(gòu)造函數(shù)着降,常用
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
Thread(Runnable target, AccessControlContext acc) {
init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
/**
* 線程名稱差油,常用
*/
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
/**
* 常用
*/
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
創(chuàng)建線程的時(shí)候常用無(wú)參和設(shè)置線程名、設(shè)置Runnable對(duì)象及組合的方式任洞。常見(jiàn)的有Thread()蓄喇、Thread(Runnable target)、Thread(String name)和Thread(Runnable target, String name)交掏,設(shè)置線程組的方式比較少見(jiàn)妆偏,除非真的有必要進(jìn)行分組管理,系統(tǒng)啟動(dòng)的時(shí)候會(huì)設(shè)置系統(tǒng)線程組盅弛、main線程組钱骂。
線程init初始化方法
java.lang.Thread
/**
* Initializes a Thread.
*
* @param g 線程組
* @param target 可被調(diào)度運(yùn)行對(duì)象
* @param name 線程名
* @param stackSize 線程堆棧大小,0表示忽略
* @param acc 線程內(nèi)部訪問(wèn)控制
* @param inheritThreadLocals 是否繼承線程本地變量
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
//設(shè)置線程名稱
this.name = name;
Thread parent = currentThread();
//默認(rèn)情況下jvm不開(kāi)啟安全管理器
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
//不是通過(guò)設(shè)置線程組的方式創(chuàng)建線程挪鹏,默認(rèn)歸屬于父線程的線程組
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
//線程組賦值
this.group = g;
//是否守護(hù)線程繼承默認(rèn)繼承自父線程
this.daemon = parent.isDaemon();
//線程優(yōu)先級(jí)见秽,也是繼承自父線程,默認(rèn)為5
this.priority = parent.getPriority();
//線程對(duì)應(yīng)類加載器
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
//Runnable實(shí)例讨盒,相當(dāng)于要運(yùn)行的task
this.target = target;
setPriority(priority);
//繼承父線程inheritThreadLocals
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
除了安全管理器的訪問(wèn)檢查稍微陌生(自行baidu解取、google),線程初始化相對(duì)比較簡(jiǎn)單返顺,做了線程相關(guān)屬性的初始化操作禀苦。
Thread和Runnable的區(qū)別與聯(lián)系
Runnable是接口,用于聲明Runnable實(shí)例是可以被線程調(diào)度運(yùn)行的创南,只定義了一個(gè)run()方法:
public interface Runnable {
public abstract void run();
}
可以通過(guò)public Thread(Runnable target)伦忠、public Thread(Runnable target, String name)構(gòu)造函數(shù)進(jìn)行創(chuàng)建線程省核。
Thread是類稿辙,實(shí)現(xiàn)了Runnable接口,這也說(shuō)明了線程是可以被獨(dú)立調(diào)度運(yùn)行和分派的气忠。
public class Thread implements Runnable {
...
/**
* 實(shí)現(xiàn)Runnable邻储,線程CPU被調(diào)度后代碼執(zhí)行入口
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
...
}
從代碼上看赋咽,直接通過(guò)無(wú)參構(gòu)造函數(shù)創(chuàng)建線程并啟動(dòng),實(shí)際上run()就相當(dāng)于一個(gè)空方法吨娜。因此脓匿,如果不以Runnable實(shí)例創(chuàng)建線程,則應(yīng)該通過(guò)繼承Thread類覆蓋run()方法的方式創(chuàng)建線程宦赠。接下來(lái)是常見(jiàn)的創(chuàng)建線程的兩種方式陪毡。
創(chuàng)建線程示例
- 繼承Thread類覆蓋run()方法的方式
public class Hello extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Hello word !");
}
}
//調(diào)用
public class ThreadCreate {
public static void main(String[] args) {
//啟動(dòng)三個(gè)線程
for (int i = 0; i < 3; i++) {
//一定要注意的是調(diào)用start()方法啟動(dòng)線程,而非調(diào)用run()方法
new Hello().start();
}
}
}
運(yùn)行結(jié)果:
Thread-0 Hello word !
Thread-2 Hello word !
Thread-1 Hello word !
-
實(shí)現(xiàn)Runnable方式創(chuàng)建線程
上邊的代碼修改為實(shí)現(xiàn)Runnable方式:
public static class Hello implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Hello word !");
}
}
public class ThreadCreate {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(new Hello()).start();
}
}
}
運(yùn)行結(jié)果:
Thread-0 Hello word !
Thread-2 Hello word !
Thread-1 Hello word !
選擇哪種方式
通過(guò)繼承的方式勾扭,Thread實(shí)現(xiàn)Runnable接口毡琉,即Thread是Runnable實(shí)例。從Thread實(shí)現(xiàn)Runnable接口代碼片段看妙色,如果target(Runnable實(shí)例)為空桅滋,run()相當(dāng)于是一個(gè)空方法,繼承覆蓋的方式實(shí)際上就是重新實(shí)現(xiàn)了Runnable接口身辨。
target被多個(gè)線程共享丐谋,同一個(gè)任務(wù)由多個(gè)線程完成,發(fā)揮多線程的優(yōu)勢(shì)煌珊。拿Thread當(dāng)Runnable實(shí)例感覺(jué)上總是有些別扭号俐。
所以,從面向接口編程的角度看推薦直接實(shí)現(xiàn)Runnable的方式創(chuàng)建線程定庵。