在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另外一種是實現Runable介面。 對於直接繼承Thread的類來說,代碼大致框架是: ? 1 2 3 4 5 6 7 8 9 10 11 class 類名 extends Thread{ 方法1; 方法2; … public voi ...
在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另外一種是實現Runable介面。
對於直接繼承Thread的類來說,代碼大致框架是:
?1 2 3 4 5 6 7 8 9 10 11 |
class 類名 extends Thread{
方法 1 ;
方法 2 ;
…
public void run(){
// other code…
}
屬性 1 ;
屬性 2 ;
…
}
|
先看一個簡單的例子:
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/**
* @author Hashsound 繼承Thread類,直接調用run方法
* */
class hello extends Thread {
private String name;
public hello() {
}
public hello(String name) {
this .name = name;
}
public void run() {
for ( int i = 0 ; i < 5 ; i++) {
System.out.println(name + "運行 " + i);
}
}
public static void main(String[] args) {
hello h1 = new hello( "A" );
hello h2 = new hello( "B" );
h1.run();
h2.run();
}
}
|
【運行結果】:
A運行 0 A運行 1 A運行 2 A運行 3 A運行 4 B運行 0 B運行 1 B運行 2 B運行 3 B運行 4
我們會發現這些都是順序執行的,說明我們的調用方法不對,應該調用的是start()方法。
當我們把上面的主函數修改為如下所示的時候:
1 2 3 4 5 6 |
public static void main(String[] args) {
hello h1= new hello( "A" );
hello h2= new hello( "B" );
h1.start();
h2.start();
}
|
然後運行程式,輸出的可能的結果如下:
B運行 0 B運行 1 B運行 2 A運行 0 A運行 1 A運行 2 B運行 3 B運行 4 A運行 3 A運行 4
因為需要用到CPU的資源,所以每次的運行結果基本是都不一樣的,呵呵。
註意:雖然我們在這裡調用的是start()方法,但是實際上調用的還是run()方法的主體。
那麼:為什麼我們不能直接調用run()方法呢?
我的理解是:線程的運行需要本地操作系統的支持。
如果你查看start的源代碼的時候,會發現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0 || this != me)
throw new IllegalThreadStateException();
group.add( this );
start0();
if (stopBeforeStart) {
stop0(throwableFromStop);
}
}
private native void start0();
|
註意我用紅色加粗的那一條語句,說明此處調用的是start0()。並且這個這個方法用了native關鍵字,次關鍵字表示調用本地操作系統的函數。因為多線程的實現需要本地操作系統的支持。
但是start方法重覆調用的話,會出現java.lang.IllegalThreadStateException異常。
通過實現Runnable介面:
大致框架是:
來先看一個小例子吧:
?1 2 3 4 5 6 7 8 9 10 11 |
class 類名 implements Runnable{
方法 1 ;
方法 2 ;
…
public void run(){
// other code…
}
屬性 1 ;
屬性 2 ;
…
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/**
* @author Hashsound 實現Runnable介面
* */
class hello implements Runnable {
public hello() {
}
public hello(String name) {
this .name = name;
}
public void run() {
for ( int i = 0 ; i < 5 ; i++) {
System.out.println(name + "運行 " + i);
}
}
public static void main(String[] args) {
hello h1= new hello( "線程A" );
Thread demo= new Thread(h1);
hello h2= new hello( "線程B" );
Thread demo1= new Thread(h2);
demo.start();
demo1.start();
}
private String name;
}
|
【可能的運行結果】:
線程A運行 0 線程B運行 0 線程B運行 1 線程B運行 2 線程B運行 3 線程B運行 4 線程A運行 1 線程A運行 2 線程A運行 3 線程A運行 4
關於選擇繼承Thread還是實現Runnable介面?
其實Thread也是實現Runnable介面的:
1 2 3 4 5 6 7 8 |
class Thread implements Runnable {
//…
public void run() {
if (target != null ) {
target.run();
}
}
}
|
其實Thread中的run方法調用的是Runnable介面的run方法。不知道大家發現沒有,Thread和Runnable都實現了run方法,這種操作模式其實就是代理模式。
Thread和Runnable的區別:
如果一個類繼承Thread,則不適合資源共用。但是如果實現了Runable介面的話,則很容易的實現資源共用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/**
* @author Rollen-Holt 繼承Thread類,不能資源共用
* */
class hello extends Thread {
public void run() {
for ( int i = 0 ; i < 7 ; i++) {
if (count > 0 ) {
System.out.println( "count= " + count--);
}
}
}
public static void main(String[] args) {
hello h1 = new hello();
hello h2 = new hello();
hello h3 = new hello();
h1.start();
h2.start();
h3.start();
}
private int count = 5 ;
}
|
【運行結果】:
count= 5 count= 4 count= 3 count= 2 count= 1 count= 5 count= 4 count= 3 count= 2 count= 1 count= 5 count= 4 count= 3 count= 2 count= 1
大家可以想象,如果這個是一個買票系統的話,如果count表示的是車票的數量的話,說明並沒有實現資源的共用。
我們換為Runnable介面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class MyThread implements Runnable{
private int ticket = 5 ; //5張票
public void run() {
for ( int i= 0 ; i<= 20 ; i++) {
if ( this .ticket > 0 ) {
System.out.println(Thread.currentThread().getName()+ "正在賣票" + this .ticket--);
}
}
}
}
public class lzwCode {
public static void main(String [] args) {
MyThread my = new MyThread();
new Thread(my, "1號視窗" ).start();
new Thread(my, "2號視窗" ).start();
new Thread(my, "3號視窗" ).start();
}
}
|
【運行結果】:
count= 5 count= 4 count= 3 count= 2 count= 1
總結一下吧:
實現Runnable介面比繼承Thread類所具有的優勢:
1):適合多個相同的程式代碼的線程去處理同一個資源
2):可以避免java中的單繼承的限制
3):增加程式的健壯性,代碼可以被多個線程共用,代碼和數據獨立。
所以,本人建議大家儘量實現介面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/**
* @author Hashsound
* 取得線程的名稱
* */
class hello implements Runnable {
public void run() {
for ( int i = 0 ; i < 3 ; i++) {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
hello he = new hello();
new Thread(he, "A" ).start();
new Thread(he, "B" ).start();
new Thread(he).start();
}
}
|
【運行結果】:
A A A B B B Thread-0 Thread-0 Thread-0
說明如果我們沒有指定名字的話,系統自動提供名字。
提醒一下大家:main方法其實也是一個線程。在java中所以的線程都是同時啟動的,至於什麼時候,哪個先執行,完全看誰先得到CPU的資源。
在java中,每次程式運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因為每當使用java命令執行一個類的時候,實際上都會啟動一個JVM,每一個jVM實習在就是在操作系統中啟動了一個進程。
判斷線程是否啟動
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/**
* @author Hashsound 判斷線程是否啟動
* */
class hello implements Runnable {
public void run() {
for ( int i = 0 ; i < 3 ; i++) {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he);
System.out.println( "線程啟動之前---》" + demo.isAlive());
demo.start();
System.out.println( "線程啟動之後---》" + demo.isAlive());
}
}
|
【運行結果】
線程啟動之前---》false 線程啟動之後---》true Thread-0 Thread-0 Thread-0
主線程也有可能在子線程結束之前結束。並且子線程不受影響,不會因為主線程的結束而結束。
線程的強制執行:
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/**
* @author Hashsound 線程的強制執行
* */
class hello implements Runnable {
public void run() {
for ( int i = 0 ; i < 3 ; i++) {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he, "線程" );
demo.start();
for ( int i= 0 ;i< 50 ;++i){
if (i> 10 ){
try {
demo.join(); //強制執行demo
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println( "main 線程執行-->" +i);
}
}
}
|
【運行的結果】:
main 線程執行-->0 main 線程執行-->1 main 線程執行-->2 main 線程執行-->3 main 線程執行-->4 main 線程執行-->5 main 線程執行-->6 main 線程執行-->7 main 線程執行-->8 main 線程執行-->9 main 線程執行-->10 線程 線程 線程 main 線程執行-->11 main 線程執行-->12 main 線程執行-->13 ...
線程的休眠:
/** * @author Hashsound 線程的休眠 * */ class hello implements Runnable { public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + i); } } public static void main(String[] args) { hello he = new hello(); Thread demo = new Thread(he, "線程"); demo.start(); } }<