Java中建立線程最常用的方法有繼承Thread類和實作Runnable兩種。Thread類實際也是實作了Runnable方法,由于無法繼承多個父類但是可以繼承多個接口,所有建立程序大多是實作Runnable接口
- 繼承Thread類
- 建立線程示例
- Thread類源碼分析
- 實作Runnable接口
- 建立線程示例
- Runnable接口源碼分析
1 繼承Thread類
1.1 建立線程示例
例如有15張票,有三個視窗,每個視窗賣五張票,可以使用繼承Thread類實作多線程處理。
import java.io.*;
import java.lang.Thread;
public class ExtendThread {
public static class MultiThread extends Thread {
private int total = ;
private String name;
MultiThread(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void run () {
while (total > ) {
System.out.println("Ticket:" + total-- + " is saled by Thread:" + Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MultiThread mt1 = new MultiThread("Thread1");
MultiThread mt2 = new MultiThread("Thread2");
MultiThread mt3 = new MultiThread("Thread3");
mt1.start();
mt2.start();
mt3.start();
}
}
運作結果如下:
Ticket: is saled by Thread:Thread2
Ticket: is saled by Thread:Thread1
Ticket: is saled by Thread:Thread3
Ticket: is saled by Thread:Thread3
Ticket: is saled by Thread:Thread3
Ticket: is saled by Thread:Thread1
Ticket: is saled by Thread:Thread2
Ticket: is saled by Thread:Thread1
Ticket: is saled by Thread:Thread3
Ticket: is saled by Thread:Thread3
Ticket: is saled by Thread:Thread1
Ticket: is saled by Thread:Thread1
Ticket: is saled by Thread:Thread2
Ticket: is saled by Thread:Thread2
Ticket: is saled by Thread:Thread2
1.2 Thread類源碼分析
1.2.1 類聲明
從類聲明可以看出Thread實作了Runnable接口。
1.2.2 構造函數
Thread類的構造函數有8個,這裡隻介紹他的無參構造函數Thread(),其他構造函數可以到http://docs.oracle.com/javase/7/docs/api/學習了解。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), );
}
由init得第三個參數可以看出線程名稱命名規則是Thread-加上線程數組合。 init函數的内部實作如下:
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
*/
//ThreadGroup:線程組表示一個線程的集合。此外,線程組也可以包含其他線程組。線程組構成一棵樹,在樹中,除了初始線程組外,每個線程組都有一個父線程組。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
Thread parent = currentThread();
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) {
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;
//每個線程都有一個優先級,高優先級線程的執行優先于低優先級線程。每個線程都可以或不可以标記為一個守護程式。當某個線程中運作的代碼建立一個新 Thread 對象時,該新線程的初始優先級被設定為建立線程的優先級,并且當且僅當建立線程是守護線程時,新線程才是守護程式。
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
this.name = name.toCharArray();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext = AccessController.getContext();
this.target = target;
setPriority(priority);
if (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();
}
1.2.3 run()方法
run定義了線程實際完成的功能,具體源碼如下:
public void run() {
if (target != null) {
target.run();
}
}
target是接口Runnable實作的引用,由于run方法并未做任何實作,是以繼承Thread類必須實作run方法。
1.2.4 start()方法
start方法作用為啟動一個線程,源碼如下:
public synchronized void start() {
if (threadStatus != || this != me)
throw new IllegalThreadStateException();
group.add(this);
start0();
if (stopBeforeStart) {
stop0(throwableFromStop);
}
}
start方法内部調用了本地方法start0建立線程,在建立線程之前會檢查目前線程對象是否已經運作過start方法,確定一個線程對象隻會運作一次start方法。如果多次運作start方法,就會導緻有多個線程同時操作相同的堆棧計數器等,導緻無法預期的結果。
1.2.5 yield()、wait()、sleep() 方法
yield方法使正在運作的線程變成就緒狀态,建議先運作其他線程。但是這種方式隻是建議,并不一定會讓其他線程先運作,也有可能目前線程繼續運作,yield方法隻會讓相同優先級的線程優先執行。
wait方法不屬于Thread類,他是Object類的方法。這個方法釋放線程鎖,知道收到notify通知為止。
sleep方法會使線程休眠一段時間,但是休眠期間不會主動釋放cpu資源。
這裡面内容挺多,忙完這段時間,把和這幾個方法相關的線程排程、同步鎖等知識溫習分享下。
1.2.6 join()方法
join方法的作用是強行運作要join的線程,阻塞目前線程知道join的線程執行完畢。如下示例:
import java.io.*;
import java.lang.Thread;
public class TestJoin {
public static class MultiThreadA extends Thread {
private String name;
private int count = ;
public MultiThreadA(String name) {
this.name = name;
}
@Override
public void run() {
while (count-- > ) {
try {
Thread.currentThread().sleep();
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(name + count);
}
}
}
public static class MultiThreadB extends Thread {
private String name;
private int count = ;
MultiThreadA a;
public MultiThreadB(MultiThreadA a) {
// TODO Auto-generated constructor stub
this.a = a;
}
@Override
public void run() {
try {
Thread.currentThread().sleep();
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("Begin ThreadB");
try {
a.join();
} catch (InterruptedException e) {
// TODO: handle exception
System.out.println("getException");
}
System.out.println("End ThreadB");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MultiThreadA tA = new MultiThreadA("ThreadA");
MultiThreadB tB = new MultiThreadB(tA);
tA.start();
tB.start();
}
}
執行結果如下:
ThreadA4
Begin ThreadB
ThreadA3
ThreadA2
ThreadA1
ThreadA0
End ThreadB
ThreadA首先列印了一條,然後ThreadB運作,遇到a.join()後運作ThreadA直到運作結束才會再次運作ThreadB。
2、實作Runnable接口
通過實作Runnable接口可以建立線程,實作的過程和Thread類内部實作很相似。
2.1 建立線程示例
Thread類示範了每個視窗售票不互相影響,各自賣五張票。如果需要三個視窗協同賣5張票,可以通過Runnalble共享變量,示例如下:
import java.io.*;
import java.lang.Thread;
public class TestRunnable {
public static class MyThread implements Runnable {
private String name;
private int total = ;
public MyThread(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
@Override
public synchronized void run() {
try {
Thread.currentThread().sleep();
} catch (Exception e) {
// TODO: handle exception
}
while (total > ) {
System.out.println("ticket:" + total + " is sold!");
total--;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread mt = new MyThread("myThread");
Thread a = new Thread(mt);
Thread b = new Thread(mt);
Thread c = new Thread(mt);
a.start();
b.start();
c.start();
}
}
運作結果如下:
ticket: is sold!
ticket: is sold!
ticket: is sold!
ticket: is sold!
ticket: is sold!
三個線程共同完成5張票的售賣。
2.2 Runnable源碼分析
Runnable接口中隻有一個抽象run方法,是以不管是實作Runnable接口或者繼承Thread類都需要重寫run方法。
public interface Runnable {
public abstract void run();
}
參考: