黑馬程序員——Java基礎---多線程

-----------android培訓java培訓、java學習型技術博客、期待與您交流!------------ 

概述

1 進程

        正在運行的程序,是系統進行資源分配和調用的獨立單位。

 每一個進程都有它自己的內存空間和系統資源。

2、線程

         是進程中的單個順序控制流,是一條執行路徑

一個進程如果只有一條執行路徑,則稱爲單線程程序。

一個進程如果有多條執行路徑,則稱爲多線程程序。

3、Java程序運行原理

   java 命令會啓動 java 虛擬機,啓動 JVM,等於啓動了一個應用程序,也就是啓動了一個進程。該進程會自動啓動一個 “主線程” ,然後主線程去調用某個類的 main 方法。所以 main方法運行在主線程中。在此之前的所有程序都是單線程的。

   並且要注意的是,jvm虛擬機的啓動是多線程的,因爲除了主線程之外還會啓動負責垃圾回收機制的線程。

 

線程的創建

       1 繼承Thread類。又稱爲繼承方式。

創建步驟:

        a,定義類繼承Thread。

        b,複寫Thread中的run方法。

             目的:將自定義代碼存儲在run方法中,讓線程運行。

        c,創建定義類的實例對象。相當於創建一個線程。

        d,用該對象調用線程的start方法。該方法的作用是:啓動線程,調用run方法。

 

發現運行結果每次都不同。

 因爲多個線程都在獲取cpu的執行權。cpu執行到誰,誰就運行。

 明確一點,在某一時刻,只能有一個程序在運行。(多核除外)

 cpu在做着快速的切換,已達到看上去是同時運行的效果。

 我們可以形象把多線程的運行行爲看做是在互相搶奪cpu的執行權。

 

 這就是多線程的一個特性:隨機性。誰搶到誰執行,至於執行多長,由cpu說的算

 

 爲什麼要覆蓋run方法?

 Thread類用於描述線程。

 該類就定義了一個功能,用於存儲線程要運行的代碼。該存儲功能就是run方法

 也就是說Thread類中的run方法,用於存儲線程要運行的代碼

 

 start調用底層讓控制單元執行

 run僅僅是封裝線程要運行的代碼

程序示例:

       class Demo extends Thread{

public  void run(){

System.out.println("demo run");

}

}

class ThreadDemo{

public static void main(String[] args){

Demo d  = new Demo();//創建好一個線程

d.start();//開啓線程並執行該線程的run方法

d.run();//僅僅是對象調用方法。而線程創建了並沒有運行

}

測試用例:

/*

創建兩個線程,和主線程交替運行。

*/

 

class ThreadTest{

public static void main(String[] args){

Test tt = new Test("one");

Test ts = new Test("two");

tt.start();

ts.start();

for (int i = 0;i<60 ;i++ ){

System.out.println("main....."+i);

}

}

}

class Test extends Thread{

private String name;

Test(String name){

this.name = name;}

public void run(){

for (int i = 0 ;i<60 ;i++ ){

System.out.println(name+"run....."+i);

}

}

}

如何獲取和設置線程名稱:

線程都有自己默認的名稱。

Thread-編號  該編號從0開始

static Thread currentThread():獲取當前線程對象

 

getName():獲取線程名稱

public final String getName()

public final void setName(String name)

其實通過構造方法也可以給線程起名字

演示用例:

class Test extends Thread{

//private String name;

Test(String name){

//this.name = name;

super(name);//自定義線程名稱

}

public void run(){

for (int i=0;i<60 ;i++ ){

System.out.println((Thread.currentThread()==this)this.getName()+"run....."+i);

}

}

class ThreadTest{

public static void main(String[] args){

Test t1 = new Test("one");

Test t2 = new Test("two");

t1.start();

t2.start();

for (int i=0;i<60 ;i++ ){

System.out.println("main......"+i);

}

}

線程調度:

線程有兩種調度模型:

分時調度模型   所有線程輪流使用 CPU 的使用權,平均分配每個線程佔用 CPU 的時間片

搶佔式調度模型   優先讓優先級高的線程使用 CPU,如果線程的優先級相同,那麼會隨機選擇一個,優先級高的線程獲取的 CPU 時間片相對多一些。 

Java使用的是搶佔式調度模型。

 

線程的運行狀態:

   被創建:等待啓動,調用start啓動。只要有進程,線程就不會結束。

         運行狀態:具有執行資格和執行權。

         臨時狀態(阻塞):有執行資格,但是沒有執行權。

         凍結狀態:遇到sleep(time)方法和wait()方法時,失去執行資格和執行權,sleep方法時間到或者調用notify()方法時,獲得執行資格,變爲臨時狀態。

         消忙狀態:stop()方法,或者run方法結束。

 

2 實現Runnable接口,又稱爲實現方式。

        使用繼承方式有一個弊端,那就是如果該類本來就繼承了其他父類,那麼就無法通過Thread類來創建線程了。所以,爲了解決這一弊端就引入了創建線程的第二種方式:實現Runnable接口

創建步驟如下:

        a,定義類實現Runnable的接口。

        b,覆蓋Runnable接口中的run方法。目的也是爲了將線程要運行的代碼存放在該run方法中。

        c,通過Thread類創建線程對象。

        d,將Runnable接口的子類對象作爲實參傳遞給Thread類的構造方法。

       爲什麼要將Runnable接口的子類對象傳遞給Thread的構造函數?

        因爲,自定義的run方法所屬的對象是Runnable接口的子類對象。所以要讓線程去指定對象的run方法,就必須明確該run方法所屬對象。

        e,調用Thread類中start方法啓動線程。start方法會自動調用Runnable接口子類的run方法。

實現方式好處:避免了單繼承的侷限性。在定義線程時,建議使用實現方式。 

  適合多個相同程序的代碼去處理同一個資源的情況,把線程同程序的代碼,數據有效分離,較好的體現了面向對象的設計思想。

程序示例:

class TicketDemo{

public static void main(String[] args){

Ticket t = new Ticket();

Thread t1 = new Thread(t);//創建線程並指定run方法所屬對象

Thread t2 = new Thread(t);

Thread t3 = new Thread(t);//new線程的同時,指定run方法所屬對象

t1.start();

t2.statt();

t3.start();

}

}

class Ticket implements Runnable{

private  int tick = 100;

public void run(){

while (true){

System.out.println(Thread.currentThread().getName()+"sale:"+tick--);

}

}

}

 

兩種方法的區別:

實現方式:避免了單繼承的侷限性。

在定義線程時,建議使用實現方式

繼承Thread類:線程代碼存放在Thread子類run方法中

實現Runnable:線程代碼存放在接口子類的run方法中    

   

線程安全問題

1、導致安全問題的出現的原因:

        當多條語句在操作同一線程共享數據時,一個線程對多條語句只執行了一部分,還沒用執行完,另一個線程參與進來執行。導致共享數據的錯誤。

簡單的說就兩點:

        a、多個線程訪問出現延遲。

        b、線程隨機性    。

注:線程安全問題在理想狀態下,不容易出現,但一旦出現對軟件的影響是非常大的。

2、解決辦法

基本思想:讓程序沒有安全問題的環境。

       把多個語句操作共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行即可。

        java中對於多線程的安全問題提供了專業的解決方式——synchronized(同步)

        同步代碼塊

        格式:

synchronized(對象){需要同步的代碼;}

 同步可以解決安全問題的根本原因就在那個對象上。該對象如同鎖的功能。持有鎖的線程可以在同步中執行。沒有持有鎖的線程即使獲取cpu的執行權,也進不去,因爲沒有獲取鎖。

同步的前提:

1、必須要有兩個或者以上的線程。

2、必須是多個線程使用同一個鎖。

 

必須保證同步中只能有一個線程在進行

 

好處:解決了多線程的安全問題

弊端:對鎖進行判斷,較爲消耗資源(在允許消耗範圍內),

演示用例:

class {

public static void main(String[] args){

}

}

class Ticket implements Runnable{

private int tick = 100;

Object obj = new Object();

public void run(){

while (true){

synchronized (obj){

if (tick>0){

try{

Thread.sleep(10);

}

catch (Exception e){

}

}

}

        同步函數

        格式:

                在函數上加上synchronized修飾符即可。

演示用例:

class BankDemo{

public static void main(String[] args){

Cus c = new Cus();

Thread t1= new Thread(c);

Thread t2 = new Thread(c);

t1.start();

t2.start();

}

}

class  Bank{

private int sum;

//Object obj = new Object();

public synchronized void add(int n){

//synchronized{//同步在操作共同數據的多態語句

sum = sum+n;

 

System.out.println("sum="+sum);

// }

}

class Cus implements Runnable{

private Bank b = new Bank();

public void run(){

for (int i = 0;i<3 ;i++ ){

b.add(100);

}

}        

那麼同步函數用的是哪一個鎖呢?

        函數需要被對象調用。那麼函數都有一個所屬對象引用。就是this。所以同步函數使用的鎖是this。

驗證:

使用兩個線程賣票

一個再同步代碼塊中

一個再同步函數中

都在執行賣票動作,若同步就不會出現錯誤的票

class {

public static void main(String[] args){

Ticket t = new Ticket();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.start();

try{

Thread.sleep(p);

}

catch (Exception e){

}

t.flag = false;

t2.start();

}

}

class Ticket implements Runnable{

private int tick = 1000;

Object obj = new Object();

boolean flag = ture;

public void run(){

if (flag){

while (true){

synchronized (this){//obj

if (tick>0){

try{

Thread.sleep(10);

}

catch (Exception e){

} System.out.println(Thread.currentThread().getName()+"....code...."+tick--);

}

}

}

}else

while (true){

show();

}

}

public synchronized void show(){

if (tick>0){

try{

Thread.sleep(10);

}

catch (Exception e){

} System.out.println(Thread.currentThread().getName()+".....show..."+tick--);

}

}

}

}

如何尋找多線程中的安全問題

        a,明確哪些代碼是多線程運行代碼。

        b,明確共享數據。

        c,明確多線程運行代碼中哪些語句是操作共享數據的。

 

靜態函數的同步方式

        如果同步函數被靜態修飾後,使用的鎖是什麼呢?

        通過驗證,發現不在是this。因爲靜態方法中也不可以定義this。靜態進內存時,內存中沒有本類對象,但是一定有該類對應的字節碼文件對象。如:

        類名.class 該對象的類型是Class

這就是靜態函數所使用的鎖。而靜態的同步方法,使用的鎖是該方法所在類的字節碼文件對象。類名.class(內存中唯一)。

演示用例:

  class {

public static void main(String[] args){

Ticket t = new Ticket();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.start();

try{

Thread.sleep(p);

}

catch (Exception e){

}

t.flag = false;

t2.start();

}

}

class Ticket implements Runnable{

private static int tick = 1000;

//Object obj = new Object();

boolean flag = ture;

public void run(){

if (flag){

while (true){

synchronized (Ticket.class){//obj

if (tick>0){

try{

Thread.sleep(10);

}

catch (Exception e){

}

System.out.println(Thread.currentThread().getName()+"....code...."+tick--);

}

}

}

}else

while (true){

show();

}

}

public static synchronized void show(){

if (tick>0){

try{

Thread.sleep(10);

}

catch (Exception e){

} System.out.println(Thread.currentThread().getName()+".....show..."+tick--);

}

}

}

}

單例設計模式——懶漢式

class Single{//保證一個類內存的唯一性

private static Single s = null;//s爲共享數據,若存在多個線程併發訪問getInstance方法,有多條語句在操作s

private Single(){}

public static synchronized Single getInstance{

if (s == null){

s = new Single();

return s; 

}

AB就不能進,A判斷滿足,創建對象,B進判斷不爲null,直接使用s

懶漢式加了同步每次都要判斷鎖,會比較低效

-------------------------------------------------------------------------------

public static  Single getInstance{

if (s = =null){

synchronizedSingle.class{

if (s == null){

只要有一個初始化,以後的救不用再判斷鎖,因爲不滿足null

s = new Single();

return s; }

}

懶漢式的特點在於延遲加載,會存在問題。

多線程訪問時會產生安全問題,可以加同步解決,同步函數,同步代碼塊都可以

但是有點低效,用雙重判斷的方式可以解決效率的問題,加同步的時候使用的鎖

爲該類所屬的字節碼文件對象

死鎖

是指兩個或者兩個以上的線程在執行的過程中,因爭奪資源產生的一種互相等待現象

        當同步中嵌套同步時,就有可能出現死鎖現象。

演示用例:

/*同步代碼塊的鎖是obj,同步函數的鎖是this

this鎖中有obj鎖,obj鎖中有this*/

class Test{

private boolean flag;

Tead (boolean flag){this.flag = flag;}

public void run(){if (flag){

while (true){

synchronized(MyLock.locka){

System.out.println("if locka");

synchronized(MyLock.lockb){

System.out.println("if lockb");}

}

}}else{

while (){

synchronized(MyLock.lockb){

System.out.println("else lockb");

synchronized(MyLock.locka){

System.out.println("else locka");

}

}}

}}

}

class MyLock{

Object locka = new Object();

Object lockb = new Object();

}

class DeadLockTest{

public static void main(String[] args){

Thread t1 = new Thread(new Test);

}

線程間通信

        其實就是多個線程在操作同一個資源,但是操作的動作不同。

 

演示用例:

class {

public static void main(String[] args){

Res r = new Res();

Input in = new Input(r);

Output out = new Output(r);

Thread t1 = new Thread(in);

Thread t2 = new Thread(out);

t1.start();

t2.satrt();

}

}

class Res{

String name;

String sex;

}

class Input implements Runnable{

private Res r ;

Input (Res r){this.r = r;}

public void run(){

int x = 0

while (true){

synchronized(r){//保證鎖相同

if (x == 0){

r.name = "";

r.sex = "";}

else{

r.name = "";

r.sex = "";}

x = (x+1)%2;

}//只同步一個無法解決問題

}

}

}

class Output implements Runnable{

private Res r ;

Output (Res r){this.r = r;}

public void run(){

while (true){

synchronized(r){

System.out.println(r.name+""+r.sex);

}//都在操作同一個對象

}

}

}

 

 幾個小問題:

        1wait(),notify(),notifyAll(),用來操作線程爲什麼定義在了Object類中?

                a,這些方法存在與同步中。

                b,使用這些方法時必須要標識所屬的同步的鎖。同一個鎖上wait的線程,只可以被同一個鎖上的notify喚醒。

                c,鎖可以是任意對象,所以任意對象調用的方法一定定義Object類中。

        2wait(),sleep()有什麼區別?

              wait():釋放cpu執行權,釋放鎖。

              sleep():釋放cpu執行權,不釋放鎖。

        3爲甚麼要定義notifyAll?

        因爲在需要喚醒對方線程時。如果只用notify,容易出現只喚醒本方線程的情況。導致程序中的所以線程都等待。

等待喚醒機制:


class {

public static void main(String[] args){

new Thread(new Input(r)).start();

new Thread(new Output(r)).start(); 

}

}

class Res{

private String name;

private String sex;

private boolean flag = false;

public synchronized void set(){

if (flag){

try{

this.wait();

}

catch (Exception e){

}

this.name = name;

this.sex = sex;}

flag = true;

this.notify();

}//可能存在安全問題,賦值需要被同步,非靜態同步函數,鎖爲this

public synchronized void out(){

if (

1flag){

try{

this.wait();

}

catch (Exception e){

}

System.out.println(name+"..........."+sex);

flag = false;

this.notify();

}//兩個線程,也需要同步

 

}

class Input implements Runnable{

private Res r ;

Input (Res r){this.r = r;}

public void run(){

int x = 0

while (true){

 

if (x == 0){

r.set("","");}

else{

r.set("","");}

x = (x+1)%2;

 

}

}

}

}

class Output implements Runnable{

private Res r ;

Output (Res r){this.r = r;}

public void run(){

while (true){

r.out();

}

}

}

線程池

程序啓動一個新線程成本是比較高的,因爲它涉及到要與操作系統進行交互。而使用線程池可以很好的提高性能,尤其是當程序中要創建大量生存期很短的線程時,更應該考慮使用線程池。

線程池裏的每一個線程代碼結束後,並不會死亡,而是再次回到線程池中成爲空閒狀態,等待下一個對象來使用。

 

JDK1.5中提供了多線程升級解決方案。

/*notifyAll不只喚醒了對方,連本方也被喚醒,

現在要求只喚醒對方線程*/

/*JDK1.5開始

java.util.concurrent.locks

|--Lock//替代了synchronized方法和語句的使用,接口

|--lock();獲取鎖

|--unlock();釋放鎖

|--Condition//替代了Object監視器方法的使用,notify,notifyAll,接口

|--await();

|--singnal();

|--singnalAll();

 

提供 了多線程的升級解決方案升級解決方案的示例:

class {

public static void main(String[] args){

Resource r = new Resource();

Producer pro = new Producer(r);

Consumer con = new Consumer(r);

Thread t1 = new Thread(pro);

Thread t2 = new Thread(con);

t1.start();

t2.start();

}

}

class Resource{

private String name;

private int count = 1;

private boolean flag = false;

 

private Lock lock = new ReentrantLock();//創建鎖

private Condition con_pro = lock.newCondition();//wait應該定義在同步代碼塊中,同步語句塊有鎖,每個wait都要標識自己所屬的鎖

private Condition con_con = lock.newCondition();

 

public  void set(String name)throws InterruptedException{

Lock.lock();//拿到鎖,進入,執行被鎖代碼,過程中拋出異常,程序結束,功能結束,沒有讀到unlock,說明沒有放鎖,其他進程就無法進入了,所以一定要放鎖

try{

while (flag){//生產者進入,判斷爲真,執行/再回來判斷不爲真,等待

con_pro.await();

this.name = name+""+count++;//帶着編號設置數據

System.out.println(Thread.currentThread().getName()+"...生產者..."+this.name);

flag = true;

con_con.signal();}//喚醒消費者

finally{

Lock.unlock();}

public void out()throws InterruptedException{

Lock.lock();//消費者進入,拿到鎖,判斷不爲真,執行/再回來判斷爲真,等待

try{

while (!flag){

con_con.await();

System.out.println(Thread.currentThread().getName()+"...消費者..."+this.name);

flag = false;

con_pro.signal();}//喚醒生產者

finally{

Lock.unlock();}

}

class Producer implements Runnable{

private Resource res;

Producer(Resource res){

this.res = res;

}

public void run(){

while (true){

try{

res.set("+商品+");

}

catch ( InterruptedException e){

}

}

}

class Consumer implements Runnable{

private Resource res;

Consumer(Resource res){

this.res = res;

}

public void run(){

while (true){

try{

res.out();

}

catch (InterruptedException e){

}

}

} 

守護線程

setDaemon()

將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,java虛擬機退出。

該方法必須在啓動線程前調用。

 

 

我們所看的都是前臺線程,當我們把某些線程標記成後臺線程後,它就就被了一些特殊的含義,後臺線程的特點就是,開啓後和前臺線程共同搶奪cpu的執行權,與前臺線程的區別在於執行結束:

當所有的前臺線程都結束時,後臺線程會自動結束。

演示用例:

class StopThreadDemo{

public static void main(String[] args){

StopThread st = new StopThread();

Thread t1 = new Thread(st);

Thread t2 = new Thread(st);

t1.setDaemon(true);

t2.setDaemon(true);//程序結束

--------------------------------

t1.start();

t2.start();//程序掛起

int num = 0;

while (true){

if (num++ == 60){

//st.changeFlag();

t1.interrupt();

t2.interrupt();

break;

}

System.out.println(Thread.currentThread().getName()+"......."+num);

}

System.out.println("over");

}

}

class StopThread implements Runnable{

private boolean flag = true;

public synchronized void run(){

while (flag){//線程0進入,拿到鎖,凍結,釋放資格;線程1進入,釋放資格

try{

wait();

}

catch (InterruptedException e){

System.out.println(Thread.currentThread().getName()+".........Exception");

flag = false;

}

System.out.println(Thread.currentThread().getName()+".........run");

}

}

public void changeFlag(){

flag = false;

}

}//主線程開啓了兩個線程後與兩個線程一塊搶奪資源,主線程是前臺

停止線程

        停止線程

1、定義循環結束的標記

因爲線程運行代碼一般都是循環,只要控制了循環即可

2、使用interrupt(中斷)方法

該方法是結束線程的凍結狀態,使線程回到運行狀態中來

 

停止線程只有一種方法,run方法結束。

開啓多線程運行,運行代碼通常都是循環結構,只要控制住循環,就可以讓run方法結束,也就是線程結束

 演示用例:

class StopThreadDemo{

public static void main(String[] args){

StopThread st = new StopThread();

Thread t1 = new Thread(st);

Thread t2 = new Thread(st);

t1.start();

t2.start();

int num = 0;

while (true){

if (num++ == 60){

break;

}

System.out.println(Thread.currentThread().getName()+"......."+num);

}

}

}

class StopThread implements Runnable{

private boolean flag = true;

public void run(){

while (flag){

System.out.println(Thread.currentThread().getName()+".........run");

}

}

public void changeFlag(){

flag = false;

}

}

/*

特殊情況:

當線程處於凍結狀態,就不會讀取到標記,那麼線程就不會結束

 

當沒有指定方式讓凍結的線程恢復到運行狀態時,這時需要對凍結進行清除。

強制讓線程恢復到運行狀態中來。這樣就可以操作標記讓線程結束。

*/

class StopThreadDemo{

public static void main(String[] args){

StopThread st = new StopThread();

Thread t1 = new Thread(st);

Thread t2 = new Thread(st);

t1.start();

t2.start();

int num = 0;

while (true){

if (num++ == 60){

//st.changeFlag();

t1.interrupt();

t2.interrupt();

break;

}

System.out.println(Thread.currentThread().getName()+"......."+num);

}

System.out.println("over");

}

}

class StopThread implements Runnable{

private boolean flag = true;

public synchronized void run(){

while (flag){//線程0進入,拿到鎖,凍結,釋放資格;線程1進入,釋放資格

try{

wait();

}

catch (InterruptedException e){

System.out.println(Thread.currentThread().getName()+".........Exception");

flag = false;

}

System.out.println(Thread.currentThread().getName()+".........run");

}

}

public void changeFlag(){

flag = false;

}

}

toString()&Priority

打印線程名稱、優先級和線程組

打印結果:

Thread[Thread-1,5main]

                    

所以線程默認優先級是5

MAX_PRIORITY 線程可以具有的最高優先級,10

MIN_PRIORITY 線程可以具有的最低優先級,1

NORM_PRIORITY 分配給線程的默認優先級,5

setPriorityThread.MAX_PRIORITY)設置優先級

yield()暫停當前正在執行的線程對象,並執行其他線程。減緩線程執行的頻率

而且能達到線程平均運行的效果

演示用例:

class JoinDemo{

public static void main(String[] args)throws Exception{

Demo d = new Demo();

Thread t1 = new Thread(d);

Thread t2 = new Thread(d);

t1.start();

 

t1.setPriorityThread.MAX_PRIORITY;

t2.start();

for (int x = 0;x<80 ;x++ ){

System.out.println("main......"+x);

}

System.out.println("over");

}

}

class Demo implements Runnable{

public void run(){

System.out.println(Thread.currentThread().getName()+""+x);

}

}

join方法

主線程碰到誰的join等誰,誰搶到不管

A線程執行到了B線程的join方法是,A就會等待,等B線程執行完,A纔會執行。

join可以用來臨時加入線程執行

class JoinDemo{

public static void main(String[] args)throws Exception{

Demo d = new Demo();

Thread t1 = new Thread(d);

Thread t2 = new Thread(d);

t1.start();

 

t1.join();//搶奪cpu執行權,主線程放出執行權,main處於凍結狀態,t1執行結束,main才能執行。當我們在進行多線程運算時,若條件滿足我們可以臨時加入一個線程,讓該線程運算完,再繼續運行

t2.start();

 

1.join();//main開啓了·1,2,碰到1join,釋放執行權,但是1,2 存活,那麼cpu就對12交替執行,1什麼時候結束main什麼時候活

for (int x = 0;x<80 ;x++ ){

System.out.println("main......"+x);

}

System.out.println("over");

}

}

class Demo implements Runnable{

public void run(){

System.out.println(Thread.currentThread().getName()+""+x);

}

}

Thread 0執行完,maint2纔開始交替執行

開發中線程的創建:

 

擴展小知識:

class ThreadTest{

public static void main(String[] args){

new Thread(){

public void run(){

for (int x = 0;x<100 ;x++ ){

System.out.println("Thread.currentThread().getName()+""+x");

}

}

}.start();

Runnable r = new Runnable(){

for (int x = 0;x<100 ;x++ ){

public void run(){

System.out.println("Thread.currentThread().getName()+""+x");

}

}

};

new Thread(r).start();

-----------android培訓java培訓、java學習型技術博客、期待與您交流!------------

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章