㈠ java中Runnable和Thread的區別
nable和thread的區別(多線程必須用Runable)
Java中有兩種實現多線程的方式以及兩種方式之間的區別
看到一個面試題.問兩種實現多線程的方法.沒事去網上找了找答案.
網上流傳很廣的是一個網上售票系統講解.轉發過來.已經不知道原文到底是出自哪裡了.
Java中有兩種實現多線程的方式。一是直接繼承Thread類,二是實現Runnable介面。那麼這兩種實現多線程的方式在應用上有什麼區別呢?
為了回答這個問題,我們可以通過編寫一段代碼來進行分析。我們用代碼來模擬鐵路售票系統,實現通過四個售票點發售某日某次列車的100張車票,一個售票點用一個線程表示。
首先這樣編寫這個程序:
Java代碼
class ThreadTest extends Thread{
private int ticket = 100;
public void run(){
while(true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() +
"is saling ticket" + ticket--);
}else{
break;
}
}
}
}
源碼列印?
class ThreadTest extends Thread{
private int ticket = 100;
public void run(){
while(true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() +
"is saling ticket" + ticket--);
}else{
break;
}
}
}
}
main測試類:
Java代碼
public class ThreadDome1{
public static void main(String[] args){
ThreadTest t = new ThreadTest();
t.start();
t.start();
t.start();
t.start();
}
}
源碼列印?
public class ThreadDome1{
public static void main(String[] args){
ThreadTest t = new ThreadTest();
t.start();
t.start();
t.start();
t.start();
}
}
上面的代碼中,我們用ThreadTest類模擬售票處的售票過程,run方法中的每一次循環都將總票數減1,模擬賣出一張車票,同時該車票號列印出來,直接剩餘的票數到零為止。在ThreadDemo1類的main方法中,我們創建了一個線程對象,並重復啟動四次,希望通過這種方式產生四個線程。從運行的結果來看我們發現其實只有一個線程在運行,這個結果 告訴我們:一個線程對象只能啟動一個線程,無論你調用多少遍start()方法,結果只有一個線程。
我們接著修改ThreadDemo1,在main方法中創建四個Thread對象:
Java代碼
public class ThreadDemo1{
public static void main(String[] args){
new ThreadTest().start();
new ThreadTest().start();
new ThreadTest().start();
new ThreadTest().start();
}
}
源碼列印?
public class ThreadDemo1{
public static void main(String[] args){
new ThreadTest().start();
new ThreadTest().start();
new ThreadTest().start();
new ThreadTest().start();
}
}
Java代碼
class ThreadTest extends Thread{
private int ticket = 100;
public void run(){
while(true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() +
" is saling ticket" + ticket--);
}else{
break;
}
}
}
}
源碼列印?
class ThreadTest extends Thread{
private int ticket = 100;
public void run(){
while(true){
if(ticket > 0){
System.out.println(Thread.currentThread().getName() +
" is saling ticket" + ticket--);
}else{
break;
}
}
}
}
這下達到目的了嗎?
從結果上看每個票號都被列印了四次,即 四個線程各自賣各自的100張票,而不去賣共同的100張票。這種情況是怎麼造成的呢?我們需要的是,多個線程去處理同一個資源,一個資源只能對應一個對象,在上面的程序中,我們創建了四個ThreadTest對象,就等於創建了四個資源,每個資源都有100張票,每個線程都在獨自處理各自的資源。
經過這些實驗和分析,可以總結出,要實現這個鐵路售票程序,我們只能創建一個資源對象,但要創建多個線程去處理同一個資源對象,並且每個線程上所運行的是相同的程序代碼。在回顧一下使用介面編寫多線程的過程。
Java代碼
public class ThreadDemo1{
public static void main(String[] args){
ThreadTest t = new ThreadTest();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
源碼列印?
public class ThreadDemo1{
public static void main(String[] args){
ThreadTest t = new ThreadTest();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
Java代碼
class ThreadTest implements Runnable{
private int tickets = 100;
public void run(){
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() +
" is saling ticket " + tickets--);
}
}
}
}
源碼列印?
class ThreadTest implements Runnable{
private int tickets = 100;
public void run(){
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() +
" is saling ticket " + tickets--);
}
}
}
}
上面的程序中,創建了四個線程, 每個線程調用的是同一個ThreadTest對象中的run()方法,訪問的是同一個對象中的變數(tickets)的實例,這個程序滿足了我們的需求。在Windows上可以啟動多個記事本程序一樣,也就是多個進程使用同一個記事本程序代碼。
可見, 實現Runnable介面相對於繼承Thread類來說,有如下顯著的好處:
(1)適合多個相同程序代碼的線程去處理同一資源的情況,把虛擬CPU(線程)同程序的代碼,數據有效的分離,較好地體現了面向對象的設計思想。
(2)可以避免由於Java的單繼承特性帶來的局限。我們經常碰到這樣一種情況,即當我們要將已經繼承了某一個類的子類放入多線程中,由於一個類不能同時有兩個父類,所以不能用繼承Thread類的方式,那麼,這個類就只能採用實現Runnable介面的方式了。
(3)有利於程序的健壯性,代碼能夠被多個線程共享,代碼與數據是獨立的。當多個線程的執行代碼來自同一個類的實例時,即稱它們共享相同的代碼。多個線程操作相同的數據,與它們的代碼無關。當共享訪問相同的對象是,即它們共享相同的數據。當線程被構造時,需要的代碼和數據通過一個對象作為構造函數實參傳遞進去,這個對象就是一個實現了Runnable介面的類的實例。 Java中Runnable和Thread的區別更詳細的資料參考:http://e.51cto.com/course/course_id-4002.html
㈡ Java中Runnable和Thread以及Callable的區別
Runnable和Callable的區別是,
(1)Callable規定的方法是call(),Runnable規定的方法是run().
(2)Callable的任務執行後可返回值,而Runnable的任務是不能返回值得
(3)call方法可以拋出異常,run方法不可以
(4)運行Callable任務可以拿到一個Future對象,Future 表示非同步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並獲取計算的結果。計算完成後只能使用 get 方法來獲取結果,如果線程沒有執行完,Future.get()方法可能會阻塞當前線程的執行;如果線程出現異常,Future.get()會throws InterruptedException或者ExecutionException;如果線程已經取消,會跑出CancellationException。取消由cancel 方法來執行。isDone確定任務是正常完成還是被取消了。一旦計算完成,就不能再取消計算。如果為了可取消性而使用 Future 但又不提供可用的結果,則可以聲明Future<?> 形式類型、並返回 null 作為底層任務的結果。Future介面的定義如下:
Future模式
Future模式在請求發生時,會先產生一個Future憑證給發出請求的客戶,它的作用就像是Proxy物件,同時,由一個新的執行線程持續進行目標物件的生成(Thread-Per-Message),真正的目標物件生成之後,將之設定至Future之中,而當客戶端真正需要目標物件時,目標物件也已經准備好,可以讓客戶提取使用。
結合JDK的Future來看,就是你run線程後,你可以把線程的返回值賦給Future並返回一個Future對象。這時你可以立即拿到這個對象,然後進行下面的邏輯。但是如果你要get這個Future中的線程結果,就會被阻塞直到線程結束。
就相當於現在的期房,你把手續和錢都交上去了,就可以馬上拿到合同,但只有合同沒有房子。這個時候你已經是有房一族了,你可以先去買家電買裝修(走下面的其他邏輯)。但是你要把家電和裝修放進去,就必須等到房子完工(阻塞)。
㈢ java 中Thread 和Runnable有何區別
區別:Thread是類,而Runnable是介面。
抽象類和介面的區別如下:
① 在類來繼承抽象類時,只需實現部分具體方法和全部抽象方法,而實現介面則要實現裡面的全部方法。
②在介面中無成員變數,而抽象類中可有成員變數。
在Java中引進介面主要是為了解決多繼承的問題。
實現多線程主要繼承Thread 類和實現Runnable介面。
㈣ java中實現Runnable必須啟動Thread類是怎麼回事
Thread t2 = new Thread(d2)相當於Thread t2 = new Thread(new Demo("線程2"));用Thread創建線程但是必須和實現了Runnable介面的類綁定起來 這樣才能調用裡面的run方法。
㈤ java中Thread類與Runnable介面實現資源共享的疑問
public
class
ThreadNotShare
extends
Thread{
private
int
share
=
5;
public
void
run(){
for(int
i
=
0
;
i
<
50
;
i++)
if(this.share
>
0){
System.out.println("shar
=
"
+
this.share--);
}
}
public
static
void
main(String
args[])
{
ThreadNotShare
t1
=
new
ThreadNotShare();
ThreadNotShare
t2
=
new
ThreadNotShare();
ThreadNotShare
t3
=
new
ThreadNotShare();
t1.start();
t2.start();
t3.start();
}
}
上面是一個繼承Thread之後,因為由一個線程類分別構造了3個對象,所以各自有各自的數據,對象之間是數據不共享的
至於樓主的問題
例二表面看是繼承了一個Thread類,但是調用的方法依舊是把一個線程類的對象交給Thread去構造,和實現Runnable介面在本質上沒有任何區別不是嗎,都是由一個對象構造出的線程,當然是共享同一份數據了
㈥ Java中Runnable和Thread的區別
1. 一種是通過繼承Thread類,同時重寫run()方法。但是java中,只允許單繼承,也就是一個類只能繼承一個父類,使得該方式具有一定的局限性,等下就知道了。
2. 另一種是實現Runnable類介面的run()方法,再結合Thread類來實現多線程。
兩種方式最終都是通過調用start()方法來實現多線程。切記不能直接調用Thread類或Runnable對象的run()方法,因為直接調用run()方法,只會執行同一個線程中的任務,而不會啟動新線程。調用start()方法將會創建一個執行run()方法的線程。
實際開發中我們通常採用Runnable介面來實現多線程。因為實現Runnable介面比繼承Thread類有如下好處:
1. 避免繼承的局限,一個類可以繼承多個介面,但是類只能繼承一個類。
2. Runnable介面實現的線程便於資源共享。而通過Thread類實現,各自線程的資源是獨立的,不方便共享。
㈦ java,線程Thread(Runnable target)
你的理解很正確,只要是實現了Runnable介面的類都可以傳進去。
這個參數可以是API中已經有的,也可以是自己寫的。
常用的創建線程的方法有兩種,第一種就是繼承Thread類,直接new
出來就可以,Thread類本身也實現了Runnable介面。
第二種方法就是實現Runnable介面裡面的run方法。
語法正如你的代碼一樣
㈧ 如何在java程序中實現多線程使用Thread子類和實現 Runnable借口兩種方法有什麼異同
第一種:繼承Thread類,通過編寫線程繼承thread類,重寫run()方法來實現線程,這個類是在java.lang包中定義的,但是一個類只能繼承一個父類,這個方法有這個局限。
第二種:直接實現Runnable多線程介面,Runnable只有一個抽象方法run ,並沒有start()方法,也就是說要通過Thread類來啟動Runnable實現的多線程。不過這個避免了繼承的局限。一個類可以繼承多個介面。
Thread類也是Runnable介面的子類。
㈨ java中的Runnable類是什麼意思
在java中可有兩種方式實現多線程,一種是繼承Thread類,一種是實現Runnable介面;
·Thread類是在java.lang包中定義的。一個類只要繼承了Thread類同時覆寫了本類中的
run()方法就可以實現多線程操作了,但是一個類只能繼承一個父類,這是此方法的局限,
下面看例子:
package org.thread.demo;
class MyThread extends Thread{
private String name;
public MyThread(String name) {
super();
this.name = name;
}
public void run(){
for(int i=0;i<10;i++){
System.out.println("線程開始:"+this.name+",i="+i);
}
}
}
package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("線程a");
MyThread mt2=new MyThread("線程b");
mt1.run();
mt2.run();
}
}
但是,此時結果很有規律,先第一個對象執行,然後第二個對象執行,並沒有相互運行。在
jdk 的文檔中可以發現,一旦調用start()方法,則會通過JVM找到run()方法。下面啟動
start()方法啟動線程:
package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("線程a");
MyThread mt2=new MyThread("線程b");
mt1.start();
mt2.start();
}
};這樣程序可以正常完成互動式運行。那麼為啥非要使用start();方法啟動多線程呢?
在JDK的安裝路徑下,src.zip是全部的java源程序,通過此代碼找到Thread中的start()方
法的定義,可以發現此方法中使用了private native void start0();其中native關鍵字表
示可以調用操作系統的底層函數,那麼這樣的技術成為JNI技術(java Native Interface)
·Runnable介面
在實際開發中一個多線程的操作很少使用Thread類,而是通過Runnable介面完成。
public interface Runnable{
public void run();
}
例子:
package org.runnable.demo;
class MyThread implements Runnable{
private String name;
public MyThread(String name) {
this.name = name;
}
public void run(){
for(int i=0;i<100;i++){
System.out.println("線程開始:"+this.name+",i="+i);
}
}
};
但是在使用Runnable定義的子類中沒有start()方法,只有Thread類中才有。此時觀察
Thread類,有一個構造方法:public Thread(Runnable targer)
此構造方法接受Runnable的子類實例,也就是說可以通過Thread類來啟動Runnable實現的多
線程。(start()可以協調系統的資源):
package org.runnable.demo;
import org.runnable.demo.MyThread;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("線程a");
MyThread mt2=new MyThread("線程b");
new Thread(mt1).start();
new Thread(mt2).start();
}
}
· 兩種實現方式的區別和聯系:
在程序開發中只要是多線程肯定永遠以實現Runnable介面為主,因為實現Runnable介面相比
繼承Thread類有如下好處:->避免點繼承的局限,一個類可以繼承多個介面。
->適合於資源的共享
以賣票程序為例,通過Thread類完成:
package org.demo.dff;
class MyThread extends Thread{
private int ticket=10;
public void run(){
for(int i=0;i<20;i++){
if(this.ticket>0){
System.out.println("賣票:ticket"+this.ticket--);
}
}
}
};
下面通過三個線程對象,同時賣票:
package org.demo.dff;
public class ThreadTicket {
public static void main(String[] args) {
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
MyThread mt3=new MyThread();
mt1.start();//每個線程都各賣了10張,共賣了30張票
mt2.start();//但實際只有10張票,每個線程都賣自己的票
mt3.start();//沒有達到資源共享
}
}
如果用Runnable就可以實現資源共享,下面看例子:
package org.demo.runnable;
class MyThread implements Runnable{
private int ticket=10;
public void run(){
for(int i=0;i<20;i++){
if(this.ticket>0){
System.out.println("賣票:ticket"+this.ticket--);
}
}
}
}
package org.demo.runnable;
public class RunnableTicket {
public stati