黑馬程序員---Java中的線程
關(guān)于Java中的線程? ? ? ? ? ? ? ? ??
1. 進(jìn)程
? 進(jìn)程:正在運(yùn)行的程序,所占有內(nèi)存空間
? 程序存儲(chǔ)在硬盤,運(yùn)行時(shí)期到了內(nèi)存中
? 線程:是一個(gè)大程序中的子程序
? CPU真正執(zhí)行的是線程,子程序?qū)τ贑PU來講獨(dú)立執(zhí)行路徑,路徑就是線程
//======================================================
2. Java中創(chuàng)建線程
? 任何都是對象,線程也是對象,對象的描述類
? java.lang.Thread類
? 創(chuàng)建線程的第一種方式,繼承Thread類,重寫run方法
? 創(chuàng)建Thread子類對象
? 調(diào)用子類中的方法start()開啟線程
? ?void start()
? ? ? ? ? 使該線程開始執(zhí)行;Java 虛擬機(jī)調(diào)用該線程的 run 方法。
? JVM 本身是單線程的程序,還是多線程的程序
? 一個(gè)線程運(yùn)行我們寫的程序,一個(gè)線程不定時(shí)收取垃圾,JVM幫助你調(diào)用Windows中的功能
? 為什么繼承Thread類,入伙,繼承Thread類,子類就是一個(gè)線程了
? 為什么重寫run方法 Thread中的run方法是空方法,等待子類重寫
? 線程運(yùn)行的程序是什么,未知的,Java工程師,開放多線程技術(shù)的時(shí)候
? 不知道使用Java編程的人員會(huì)運(yùn)行哪些代碼
? 提供一個(gè)標(biāo)準(zhǔn),就是run方法:不管線程運(yùn)行哪些代碼,必須放在run中,線程就運(yùn)行你run中的代碼
/*
?* 創(chuàng)建線程第一種方式,繼承Thread類
?*/
class Demo1 extends Thread{
?public void run(){
? for(int x = 0 ; x < 50 ;x++){
? ?System.out.println("run..."+x);
? }
?}
}
public class ThreadDemo1 {
?public static void main(String[] args) {
? while(true){
? Demo1 d = new Demo1();
? d.start();//開啟線程,JVM自動(dòng)調(diào)用線程的run方法
? for(int x = 0 ; x < 50 ; x++){
? ?System.out.println("main..."+x);
? }
? }
?}
}
//======================================================
4. 線程的狀態(tài)圖,必須自己會(huì)畫
5. 線程名字的獲取和設(shè)置
? 獲取名字,Thread類的方法getName(),返回字符串
? 線程名字JVM命名的 Thread-0 Thread-1
? 在Thread子類中,直接使用父類方法getName()獲取名字
? 在不是Thread子類中,獲取線程名字
? Thread類中,提供了一個(gè)靜態(tài)方法 currentThread()返回值是一個(gè)Thread類的對象
? 方法,返回當(dāng)前線程對象,既然返回的是對象,方法調(diào)用鏈
? ?String name = Thread.currentThread().getName();
? 設(shè)置線程的名字:
? ? Thread類的方法setName()
? ? Thread類的構(gòu)造方法
? ? ? Thread(String name)傳遞線程名字
? ? Thread子類中,super語句將線程的名字送到父類構(gòu)造方法
class ThreadName extends Thread{
?ThreadName(String name){
? super(name);
?}
?public void run(){
? String name =Thread.currentThread().getName();
? System.out.println(name+" 線程ThreadName");
?}
}
public class ThreadDemo {
?public static void main(String[] args) {
? //Thread.currentThread();//返回的就是運(yùn)行main方法的線程對象
? String name = Thread.currentThread().getName();
? System.out.println(name);
? ThreadName t1 = new ThreadName("西班牙0");
? ThreadName t2 = new ThreadName("智利2");
?
?// t1.setName("小強(qiáng)");
?// t2.setName("旺財(cái)");
?
? t1.start();
? t2.start();
?}
}
//======================================================
6. 售票的一個(gè)案例
? 利用多線程模擬多窗口一起售票
? 模擬出了,卡機(jī)線程,導(dǎo)致了數(shù)據(jù)的安全問題
? 多線程操作同一個(gè)數(shù)據(jù)的時(shí)候,出現(xiàn)數(shù)據(jù)安全問題
?
? ?解決辦法:一個(gè)線程不執(zhí)行完畢,其他的線程,不能執(zhí)行
?
? Java中開放出了同步技術(shù),保證線程的安全性,會(huì)阻止其他線程進(jìn)入
? 同步代碼塊
? ? synchronized(任意對象){
? ?
? ? ? ?線程操作的共享數(shù)據(jù)
? ? }
/*
?* 模擬售票4個(gè)窗口一起出售
?* 改造成實(shí)現(xiàn)Runnable接口的方式
?*/
class Ticket implements Runnable{
?private int tickets = 100;
?private Object obj = new Object();
?public void run(){
? while(true){
? ?synchronized(obj){
? ?if(tickets > 0){
? ? //線程if判斷完畢后,休眠一段時(shí)間
? ? try{
? ? Thread.sleep(67);
? ? }catch(Exception e){}
? ? System.out.println(Thread.currentThread().getName()+"..出售第.."+tickets--);
? ?}
? ?}
? }
?}
}
public class ThreadDemo1 {
?public static void main(String[] args) {
? //創(chuàng)建Ticket對象
? Ticket t = new Ticket();
? //創(chuàng)建Thread類對象,傳遞Runnable接口的實(shí)現(xiàn)類對象
? Thread t1 = new Thread(t);
? Thread t2 = new Thread(t);
? Thread t3 = new Thread(t);
? Thread t4 = new Thread(t);
? t1.start();
? t2.start();
? t3.start();
? t4.start();
?}
}
//======================================================
7. 創(chuàng)建線程的第二種方式
? 定義類,實(shí)現(xiàn)Runnable接口
? 重寫run方法
? class A implements Runnable{
? ? ? ? public void run(){
?}
? }
? A類不再是線程類了
? 直接創(chuàng)建Thread類對象
? 構(gòu)造方法Thread(Runnable target) 接受的數(shù)據(jù)類型是Runnable接口的子類類型
? new Thread(new A());?
? 調(diào)用Thread類中的start();
? 兩個(gè)線程的創(chuàng)建方式的區(qū)別
? 繼承Thread類,實(shí)現(xiàn)Runnable接口區(qū)別
? 繼承,單繼承局限性
? 接口,多現(xiàn)實(shí),避免了單繼承的局限性
? 繼承Thread類方式,線程中的數(shù)據(jù)是對象自己的
? 實(shí)現(xiàn)接口方法,線程中的數(shù)據(jù)是共享的
? 寫多線程程序推薦使用接口方式
//======================================================
8. 同步的原理
? ? synchronized(任意對象){
? ?
? ? ? ?線程操作的共享數(shù)據(jù)
? ? }
?
? 對象,寫在了同步中
? 專業(yè)名詞,對象監(jiān)視器
? 通俗一點(diǎn):鎖
? 線程進(jìn)到同步代碼塊后,線程獲取這把鎖,這扇門永久關(guān)閉了
? 當(dāng)線程出去同步代碼塊,將鎖釋放
? 廁所原理
? 多線程操作同一個(gè)數(shù)據(jù),安全問題
? 如果是單線程,沒有數(shù)據(jù)安全問題
//======================================================
9. 模擬存錢
? 一張卡,可以多個(gè)柜臺(tái)存錢
? 余額是0,每個(gè)柜臺(tái)每次存100元
? 兩個(gè)柜臺(tái),每個(gè)存3次
? 600元,沒存一次,查看余額
? 同步方法,在方法的聲明上寫同步關(guān)鍵字
? 線程每次只有一個(gè)運(yùn)行這個(gè)方法
? 當(dāng)方法中所有代碼都是線程操作的的共享數(shù)據(jù)
? 同步方法中,鎖是什么
? 確定的是,鎖肯定有,鎖必須是對象
? 鎖是本類的對象引用this
? 方法中的同步代碼塊,鎖直接寫this
? 靜態(tài)方法中的,同步鎖是誰
? 鎖是對象!
? 靜態(tài)優(yōu)先于對象,靜態(tài)中的鎖,是本類的class文件對象
? Java中,每一個(gè)數(shù)據(jù)類型,JVM都賦予他們一個(gè)靜態(tài)成員變量
? class名字,變量的運(yùn)行結(jié)果就是類的class文件對象
? 靜態(tài)方法中的鎖,就是本類.class字節(jié)碼文件對象
/*
?* 存錢的時(shí)候
?* 卡,錢,到銀行中去,銀行柜臺(tái)存錢
?* 但是,整個(gè)Add方法中的所有代碼,都是線程操作的共享數(shù)據(jù)
?* 沒有必要同步一段代碼,同步整個(gè)方法
?*/
class Bank{
?//存錢功能,存一次,看余額
?private static int sum = 0;
?public static synchronized void add(int money){
?// synchronized(Bank.class){
? sum = sum + money;
? System.out.println(sum);
?// }
?}
}
class Customer implements Runnable{
?private Bank b = new Bank();
?public void run(){
? for(int x = 0 ; x < 3 ; x++){
? Bank.add(100);
? }
?}
}
public class ThreadDemo2 {
?public static void main(String[] args) {
? Customer c = new Customer();
? Thread t1 = new Thread(c);
? Thread t2 = new Thread(c);
? t1.start();
? t2.start();
?}
}
//==============================================================
10. 單例模式
? 懶漢有安全隱患,多線程并發(fā)訪問懶漢式的時(shí)候
? 安全問題,同步避免
? 效率很低,提高懶漢的效率
? 第一次執(zhí)行s=null
? 進(jìn)同步 創(chuàng)建對象,返回
? 第二次執(zhí)行s!=null
? 沒有必要進(jìn)同步了,直接返回
? 兩次判斷,提高效率
class Single{
?private Single(){}
?private static Single s = null;
?public static Single getInstance(){
?
? if(s == null){
?
? synchronized(Single.class){
? if( s == null)
? ?s = new Single();
? ? ? ? }
? }
?
? return s;
?}
}
class SingleThead implements Runnable{
?public void run(){
? for(int x = 0 ; x < 30 ; x++){
? ?Single s = Single.getInstance();
? ?System.out.println(s);
? }
?}
}
//==============================================================
11. 死鎖案例
? 在多線程的技術(shù)中,兩個(gè)或者兩個(gè)以上的線程,同時(shí)爭奪一個(gè)對象監(jiān)視器
? 導(dǎo)致程序的假死現(xiàn)象
? 死鎖:出現(xiàn)條件,必須多線程,爭奪一個(gè)鎖,程序中,體現(xiàn)在同步代碼塊的嵌套效果
? 死鎖的案例,面試過程中經(jīng)常被考到
//==============================================================
12. 線程的通信
? 兩個(gè)線程同時(shí)對一個(gè)資源對象,進(jìn)行賦值和取值操作
? 思考,數(shù)據(jù)安全問題怎么發(fā)生的
? 發(fā)生后,怎么解決
? 數(shù)據(jù)安全問題:線程的隨機(jī)性導(dǎo)致程序數(shù)據(jù)安全隱患
? 采用了同步技術(shù),依然沒有解決
? 當(dāng)你發(fā)現(xiàn)數(shù)據(jù)安全問題后,使用了同步技術(shù),還是不能解決
? ? 第一點(diǎn),確定程序是不是多線程在操作共享數(shù)據(jù) 確定
? ? 第二點(diǎn),使用的同步中的鎖,是同一把鎖嗎,鎖不同唯一的
? ? ? 必須將鎖變成唯一的對象,才能控制數(shù)據(jù)安全問題
? ? ? 資源對象,數(shù)據(jù)的安全性解決了
? 線程等待和喚醒的方法
? 沒有出現(xiàn)在線程描述類Thread類中
? 方法定義在了Object類中,為什么這樣設(shè)計(jì)
? 原因是鎖,鎖是一個(gè)對象,對象是通過類new出來的
? 鎖是哪一個(gè)類的對象,無法確定的
? 但是將方法寫在Object類中,所有的類的對象,都具有線程的等待與喚醒方法
? wait()方法的使用,將線程永久等待,直到有人喚醒
? wait方法必須有鎖的支持,wait方法必須寫在同步中
? 鎖是唯一的
? synchronized(r){
? ? ?wait();
? ? ?notify();
? }
? synchronized(r){
? ? ?notify()
? }
? IllegalMonitorStateException
? 異常,運(yùn)行時(shí)期的異常,拋出該異常,說明wait notify沒有鎖的支持,沒有對象監(jiān)視器
/*
?* 線程通信的代碼的優(yōu)化
?*/
//定義資源類,私有處理
class Recource{
?private String name;
?private String sex;
?private boolean flag = false;
?//提供get set方法,訪問成員變量
?public synchronized void set(String name,String sex){
? if(flag)
? ?try{
? ?this.wait();
? ?}catch(Exception e){}
? this.name = name;
? this.sex = sex;
? flag = true;
? this.notify();
?}
?public synchronized void get(){
? if(!flag)
? ?try{
? ?this.wait();
? ?}catch(Exception e){}
? System.out.println(name+"..."+sex);
? flag = false;
? this.notify();
?}
}
//輸入線程
class Input implements Runnable{
?private Recource r;
?Input(Recource r){this.r = r;}
?public void run(){
? int x = 0 ;
? while(true){
? ?if(x %2 == 0){
? ? r.set("張三", "男");
? ?}else{
? ? r.set("李四", "女");
? ?}
? ?x++;
? }
?}
}
//輸出的線程
class Output implements Runnable{
?private Recource r ;
?Output(Recource r){this.r=r;}
?public void run(){
? while(true){
? ?r.get();
? }
?}
}
public class ThreadDemo5 {
?public static void main(String[] args) {
? Recource r = new Recource();
? new Thread(new Input(r)).start();
? new Thread(new Output(r)).start();
?}
}
//==============================================================
13. wait() sleep() 導(dǎo)致線程等待,兩個(gè)方法區(qū)別在哪里(不要光看表面。)
??
? sleep(毫秒值)自動(dòng)醒來
? wait()永久等待,需要?jiǎng)e的線程喚醒
? sleep()方法是Thread類的靜態(tài)方法
? wait()方法是Object類的非靜態(tài)方法
? sleep()不需要對象鎖
? wait()必須有鎖的支持
? sleep()方法,執(zhí)行的時(shí)候線程不會(huì)釋放對象鎖
? wait()方法,執(zhí)行的時(shí)候,線程放棄對象鎖,被喚醒的時(shí)候,從新獲取對象鎖,才能運(yùn)行
//==============================================================
14. 定時(shí)器
? Java程序中,有定時(shí)功能,按照一定的時(shí)間間隔運(yùn)行指定的程序
? 定時(shí)器類,java.util.Timer
? 構(gòu)造方法
? ? Timer(boolean isDaemon) false 不是守護(hù)的線程
? ? schedule(TimerTask task, Date firstTime, long period)
? ? ? ? ? ? ? ? ? ? ? ?定時(shí)器任務(wù) ? ? ? ? ? ? ?第一次時(shí)間 ? ? ? ? 間隔
? 抽象類 TimerTask 時(shí)間任務(wù),定時(shí)器執(zhí)行的程序,寫在這個(gè)類的run方法
/*class Time extends TimerTask{
?public void run(){
? System.out.println("定時(shí)2秒鐘發(fā)送一次郵件");
?}
}*/
public class TimerDemo {
?public static void main(String[] args) {
? Timer t = new Timer(false);
?
? t.schedule(new TimerTask(){
? ?public void run(){
? ? System.out.println(""定時(shí)2秒鐘發(fā)送一次郵件"");
? ?}
? }, new Date(), 3600000);
?
? System.out.println("main...over");
?}
}
//==============================================================
15. 多線程通信,生產(chǎn)者與消費(fèi)者
? 一個(gè)產(chǎn)品,分別線程控制,一個(gè)控制生產(chǎn),一個(gè)控制消費(fèi)
? 生產(chǎn)一個(gè),消費(fèi)一個(gè),多生產(chǎn)者,多消費(fèi)者
? 多個(gè)生產(chǎn)與多個(gè)消費(fèi),全部的喚醒notifyAll()
? 喚醒以后,數(shù)據(jù)安全性還是沒解決
? 線程在wait上等待,被喚醒的時(shí)候,從Wait這里起來
? 起來以后,不會(huì)再次判斷flag是true,還是false,因此數(shù)據(jù)問題,沒喲解決
? 線程,被喚醒以后,但是判斷標(biāo)記??!
? 用的是循環(huán)的方式,解決線程倒下去后,再起來,必須還要判斷標(biāo)記
? 但是發(fā)現(xiàn)一個(gè)問題:
? ? notifyAll()喚醒了全部等待的線程
? ? 1個(gè)活的,7個(gè)等著,全部醒來
? ? 浪費(fèi)資源,能不能喚醒對方的一個(gè)線程的
? ? 生產(chǎn)者,只喚醒一個(gè)消費(fèi)者
? ? 消費(fèi)者,只喚醒一個(gè)生產(chǎn)者
? ? 喚醒本方是沒有意義,全部喚醒是浪費(fèi)資源的
? ? Java的JDK1.4版本之前,是做不到了
? ? 到了JDK1.5版本后,就可以實(shí)現(xiàn)了
? ? 提供一套新的多線程的操作方式
? ? synchronized,被替換了
? ? wait(),notify(),notifyAll(),被替換了
? ? JDK1.5的新特性,線程鎖定操作
? ? 導(dǎo)包 java.util.concurrent.locks
//==============================================================
16. JDK1.5的鎖 lock接口
? Lock接口--synchronized同步
? 同步是有鎖提供
? 接口中,定義了兩個(gè)方法 lock() unlock()
? 要同步線程的時(shí)候,使用這兩個(gè)方法,進(jìn)行鎖操作
? Lcok接口的實(shí)現(xiàn)類對象ReentrantLock
? 獲取到接口的實(shí)現(xiàn)類對象,調(diào)用方法,鎖操作
? 新的技術(shù)中,JDK提供了一個(gè)接口Condition
? 替換了原有線程方法,wait,notify
? 將線程進(jìn)行分組管理
? t1-t4 set方法,用鎖就是set方法中的鎖
? t5-t8 get方法,用鎖就是get方法中的鎖
? 一個(gè)lock接口上,可以實(shí)現(xiàn)多個(gè)Condition對象
? final Condition notFull = lock.newCondition();
? final Condition notEmpty = lock.newCondition();
? 將一個(gè)接口Lock,分成兩組管理
? Condition接口中的三個(gè)方法
? ?await() -- wait()
? ?signal() -- notify()
? ?signalAll() -- notifyAll();
/*
?* 將案例,改造成lock鎖方式實(shí)現(xiàn)功能
?*/
import java.util.concurrent.locks.*;
class Product{
?private String name;
?//定義計(jì)數(shù)器
?private int count ;
?//定義標(biāo)識
?private boolean flag = false;
?//定義Lock鎖對象
?private Lock lock = new ReentrantLock();
?//通過Lock接口,獲取Condition對象
?private Condition pro = lock.newCondition();
?private Condition cus = lock.newCondition();
?
?//定義生產(chǎn)方法
?public void set(String name){
? //獲取鎖
? lock.lock();
? while(flag)
? ?try{
? ? ?pro.await();
? ?}catch(Exception e){}
? this.name = name + count++;
? System.out.println(Thread.currentThread().getName()+"生產(chǎn)第..."+this.name);
? flag = true;
? //this.notifyAll();
? //釋放鎖
? cus.signal();
? lock.unlock();
?}
? ?//定義消費(fèi)方法
?public void get(){
? lock.lock();
? while(!flag)
? ?try{
? ?cus.await();
? ?}catch(Exception e){}
? System.out.println(Thread.currentThread().getName()+"消費(fèi)第........."+this.name);
? flag = false;
? //this.notifyAll();
? pro.signal();
? lock.unlock();
?}
}
//定義生產(chǎn)者
class Producter implements Runnable{
?private Product p ;
?Producter(Product p){this.p = p;}
?public void run(){
? while(true){
? ?p.set("鍵盤");
? }
?}
}
//定義消費(fèi)這
class Customer implements Runnable{
?private Product p ;
?Customer(Product p){this.p = p;}
?public void run(){
? while(true){
? ?p.get();
? }
?}
}
public class ThreadDemo {
?public static void main(String[] args) {
? Product p = new Product();
? Producter producter = new Producter(p);
? Customer customer = new Customer(p);
? Thread t1 = new Thread(producter);
? Thread t2 = new Thread(producter);
? Thread t3 = new Thread(producter);
? Thread t4 = new Thread(producter);
? Thread t5 = new Thread(customer);
? Thread t6 = new Thread(customer);
? Thread t7 = new Thread(customer);
? Thread t8 = new Thread(customer);
? t1.start();
? t2.start();
? t3.start();
? t4.start();
? t5.start();
? t6.start();
? t7.start();
? t8.start();
?}
}
//==========================================================
17. 線程的停止方式
? Thread類中,有一個(gè)方法stop(),過時(shí)
? 終止線程的運(yùn)行,目的結(jié)束run方法就行
? 停止線程的第一種方式,改變變量的值,結(jié)束while循環(huán),結(jié)束了run方法
??
處于等待中的線程,怎么停下
? 例子:
? ? 我有一個(gè)朋友,失眠,找了一個(gè)催眠大師(水平很高)
? ? 進(jìn)行了催眠,朋友就睡了(wait())
? ? 催眠師說,被我催眠的人,只有我能叫醒
? ? 催眠師死了,不能讓朋友永久的等待下去
? ? 拍你一板磚,醒來,收到了傷害(異常)
?
? 線程的第二種停止方式
? ? void interrupt() + 異常停下,等待中的線程
? ? 打擊線程方法,處于等待的線程,將會(huì)拋出異常
/*
?* 線程如何停止下來
?*/
class StopThread implements Runnable{
?private boolean flag = true;
?public void run(){
? while(flag){
? ?synchronized(this){
? ? try{this.wait();}catch(Exception e){
? ? ?e.printStackTrace();
? ? ?//System.out.println(e.getMessage()+"打你一板磚");
? ? ?flag = false;
? ? }
? ?System.out.println("run....");
? ?}
? }
?}
?public void setFlag(boolean flag){
? this.flag = flag;
?}
}
public class ThreadDemo1 {
?public static void main(String[] args) {
? StopThread st = new StopThread();
? Thread t = new Thread(st);
? t.start();
? for(int x = 0 ; x < 1000 ; x++){
? if(x==999)
? ? ? //st.setFlag(false);
? ?t.interrupt();
? else
? ?System.out.println("main"+x);
? }
?}
}
//==========================================================
18. 守護(hù)線程
? Thread類中。setDaemon(boolean )傳遞是true,線程守護(hù)線程
? 如果所有運(yùn)行的線程,都是守護(hù)線程,JVM退出
? 方法,必須在start開始前調(diào)用
? Feiq,開啟多個(gè)聊天窗口的時(shí)候,一旦關(guān)閉飛秋主程序,聊天窗口也就關(guān)閉了
? 聊天窗口線程,就是飛秋主線程的守護(hù)線程,一旦主線程死亡,所有的守護(hù)線程就死亡
/*
?* 守護(hù)線程
?*/
class ThreadDaemon implements Runnable{
?public void run(){
? while(true)
? System.out.println("run....");
?}
}
public class ThreadDemo2 {
?public static void main(String[] args) {
? ThreadDaemon td = new ThreadDaemon();
? Thread t = new Thread(td);
? t.setDaemon(true);
? t.start();
?}
}
//==========================================================
19. Thread中其他方法,toString() setPriority() join() yield()
? toString()繼承Object,重寫toString()
? Thread[Thread-0,5,main]
? 5 優(yōu)先級,main 線程組
? 優(yōu)先級三個(gè)級別,最低,默認(rèn),最高
? setPriority(int )設(shè)置優(yōu)先級
? 但是優(yōu)先級的效果,多核,多線程的CPU上,效果不是很明顯了
? join() 加入 等待該線程終止
? 使用join方法的線程,不停止,其他線程運(yùn)行不了
? static yield()
/*
?* 線程的讓步方法,static yield
?*/
class YieldThread implements Runnable{
?public void run(){
? for(int x = 0 ; x < 100 ; x++){
? ?Thread.yield();
? ?System.out.println(Thread.currentThread().getName()+"run.."+x);
? }
?}
}
public class ThreadDemo5 {
?public static void main(String[] args) {
? YieldThread yt = new YieldThread();
? Thread t0 = new Thread(yt);
? Thread t1 = new Thread(yt);
? t0.start();
? t1.start();
?}
}
/*
?* 等待該線程終止
?*/
class JoinThread implements Runnable{
?public void run(){
? for(int x = 0 ; x < 100; x++){
? ?System.out.println(Thread.currentThread().getName()+"run.."+x);
? }
?}
}
public class ThreadDemo4 {
?public static void main(String[] args) throws Exception{
? JoinThread jt = new JoinThread();
? Thread t0 = new Thread(jt);
? Thread t1 = new Thread(jt);
? ? ?t0.start();
? ? ?t0.join();
? ? ?t1.start();
? for(int x = 0 ;x < 100 ; x++){
? ?System.out.println("main...."+x);
? }
?}
}
/*
?* Thread中的toString()
?*/
class ThreadToString implements Runnable{
?public void run(){
? for(int x = 0 ; x < 100 ; x++){
? ?System.out.println(Thread.currentThread().getName()+"run.."+x);
? }
?}
}
public class ThreadDemo3 {
?public static void main(String[] args) {
? ThreadToString tts = new ThreadToString();
? Thread t0 = new Thread(tts);
? Thread t1 = new Thread(tts);
? t0.setPriority(Thread.MAX_PRIORITY);
? t1.setPriority(Thread.NORM_PRIORITY);
? t0.start();
? t1.start();
?// System.out.println(t);
?}
}