來源:jiansin 發(fā)布時間:2018-08-29 17:02:34 閱讀量:1021
進程是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ),是一次程序的執(zhí)行;是一個程序及其數(shù)據(jù)在處理機上順序執(zhí)行時所發(fā)生的活動;是程序在一個數(shù)據(jù)集合上運行的過程,它是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位。進程是受操作系統(tǒng)管理的基本運行單元。
線程可以理解成是在進程中獨立運行的子任務(wù)。比如QQ.exe運行時就有很多的子任務(wù)在同時運行。再如,好友視頻線程、下載文件線程、傳輸數(shù)據(jù)線程、發(fā)送表情線程等,這些不同的任務(wù)或者說功能都可以同時運行,其中每一項任務(wù)完全可以理解成是“線程”在工作,傳文件、聽音樂、發(fā)送圖片表情等功能都有對應(yīng)的線程在后臺默默地運行。
多線程的優(yōu)點:可以最大限度的利用CPU的空閑時間來處理其他的任務(wù),而CPU在這些任務(wù)之間不停的切換,由于切換的速度非???,給使用者的感受就是這些任務(wù)似乎在同時運行。所以使用多線程技術(shù)后,可以在同一時間內(nèi)運行更多不同種類的任務(wù)。
單任務(wù)的特點就是排隊執(zhí)行,也就是同步,就像在cmd中輸入一條命令后,必須等待這條命令執(zhí)行完才可以執(zhí)行下一條命令一樣。這就是單任務(wù)環(huán)境的缺點,即CPU利用率大幅降低。而多線程情況下,CPU完全可以在任務(wù)1和任務(wù)2之間來回切換,使任務(wù)2不必等到10秒再運行,系統(tǒng)的運行效率大大得到提升,這就是多線程技術(shù)的優(yōu)點,使用多線程也就是在使用異步,千萬不要把開發(fā)環(huán)境里代碼的順序當(dāng)成線程執(zhí)行的順序,線程被調(diào)用的時機是隨機的。
在Java 的JDK 開發(fā)包中,已經(jīng)自帶了對多線程技術(shù)的支持,可以很方便地進行多線程編
程。實現(xiàn)多線程編程的方式主要有兩種,一種是繼承Thread 類,另一種是實現(xiàn)Runnable 接口。
package com.mythread.www;public class MyThread extends Thread { @Override public void run() { super.run(); System.out.println("MyThread"); } }
package test;import com.mythread.www.MyThread;public class Run {public static void main(String[] args) { MyThread mythread = new MyThread(); mythread.start(); System.out.println(" 運行結(jié)束! "); } }
輸出結(jié)果
運行結(jié)束! MyThread
MyThread.java 類中的run 方法執(zhí)行的時間比較晚,這也說
明在使用多線程技術(shù)時,代碼的運行結(jié)果與代碼執(zhí)行順序或調(diào)用順序是無關(guān)的。
如果欲創(chuàng)建的線程類已經(jīng)有一個父類了,這時就不能再繼承自Thread 類了,因為Java
不支持多繼承,所以就需要實現(xiàn)Runnable 接口來應(yīng)對這樣的情況。
package myrunnable;public class MyRunnable implements Runnable { @Override public void run() { System.out.println(" 運行中!"); } public static void main(String[] args) { Runnable runnable=new MyRunnable(); Thread thread=new Thread(runnable); thread.start(); System.out.println(" 運行結(jié)束!"); } }
輸出結(jié)果
運行結(jié)束! 運行中!
那也就意味著構(gòu)造函數(shù)Thread(Runnable target) 不光可以傳入Runnable 接口的對象,還
可以傳入一個Thread 類的對象,這樣做完全可以將一個Thread 對象中的run() 方法交由其他
的線程進行調(diào)用。
自定義線程類中的實例變量針對其他線程可以有共享與不共享之分,這在多個線程之間
進行交互時是很重要的一個技術(shù)點。
不共享數(shù)據(jù)的情況
public class MyThread extends Thread { private int count = 5; public MyThread(String name) { super(); this.setName(name);// 設(shè)置線程名稱 } @Override public void run() { super.run(); while (count > 0) { count--; System.out.println(" 由 " + this.currentThread().getName() + " 計算,count=" + count); } } }
public class Run { public static void main(String[] args) { MyThread a=new MyThread("A"); MyThread b=new MyThread("B"); MyThread c=new MyThread("C"); a.start(); b.start(); c.start(); } }
一共創(chuàng)建了3 個線程,每個線程都有各自的count 變量,自己減少
自己的count 變量的值。這樣的情況就是變量不共享,此示例并不存在多個線程訪問同一個實例變量的情況。
共享數(shù)據(jù)的情況就是多個線程可以訪問同一個變量,比如在實現(xiàn)投票功能的軟件時,多個線程可以同時處理同一個人的票數(shù)。
public class MyThread extends Thread { private int count=5; @Override public void run() { super.run(); count--; System.out.println(" 由 "+Thread.currentThread().getName()+" 計算, count="+count); } }public class Run { public static void main(String[] args) { MyThread mythread=new MyThread(); Thread a=new Thread(mythread,"A"); Thread b=new Thread(mythread,"B"); Thread c=new Thread(mythread,"C"); Thread d=new Thread(mythread,"D"); Thread e=new Thread(mythread,"E"); a.start(); b.start(); c.start(); d.start(); e.start(); } }
輸出結(jié)果
由 B 計算, count=3 由 A 計算, count=3 由 C 計算, count=2 由 D 計算, count=1 由 E 計算, count=0
線程A 和B 打印出的count 值都是
3,說明A 和B 同時對count 進行處理,產(chǎn)生了“非線程安全”問
題。
在某些JVM 中,i-- 的操作要分成如下3 步:
1 取得原有i 值。 2 計算i-1。 3 對i 進行賦值。 在這3 個步驟中,如果有多個線程同時訪問,那么一定會出現(xiàn)非線程安全問題。
在這里占不解決同步問題,讀者可以自行思考。
本節(jié)將通過程序案例細化一下println() 方法與i++ 聯(lián)合使用時“有可能”出現(xiàn)的另外一種異常情況,并說明其中的原因。
package extthread;public class MyThread extends Thread { private int i = 5; @Override public void run() { System.out.println("i=" + (i--) + " threadName=" + Thread.currentThread().getName()); // 注意:代碼i-- 由前面項目中單獨一行運行改成在當(dāng)前項目中在println() 方法中直接進行打印 } }
package test;import extthread.MyThread;public class Run { public static void main(String[] args) { MyThread run = new MyThread(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); Thread t4 = new Thread(run); Thread t5 = new Thread(run); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
輸出結(jié)果
i=5 threadName=Bi=4 threadName=Ci=5 threadName=Ai=2 threadName=Di=3 threadName=E
雖然println() 方法在內(nèi)部是同步的,但i-- 的操作卻是在進入
println() 之前發(fā)生的,所以有發(fā)生非線程安全問題的概率。
/** * Prints a String and then terminate the line. This method behaves as * though it invokes <code>{@link #print(String)}</code> and then * <code>{@link #println()}</code>. * * @param x The <code>String</code> to be printed. */ public void println(String x) { synchronized (this) { print(x); newLine(); } }
currentThread() 方法可返回代碼段正在被哪個線程調(diào)用的信息。
/** * Created by Lee on 2018/7/25. */public class MyThread extends Thread { public MyThread () { System.out.println("構(gòu)造方法打印的是:" + Thread.currentThread().getName()); } @Override public void run() { System.out.println("run方法打印的是" + Thread.currentThread().getName()); } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); //thread.start(); } }
輸出結(jié)果
構(gòu)造方法打印的是:main run方法打印的是Thread-0//run方法打印的是main
再來一個比較復(fù)雜的
package mythread;public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("this.getName()=" + this.getName()); System.out.println("CountOperate---end"); } @Override public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("this.getName()=" + this.getName()); System.out.println("run---end"); } public static void main(String[] args) { CountOperate c = new CountOperate(); Thread t1 = new Thread(c); t1.setName("A"); t1.start(); } }
輸出結(jié)果
CountOperate---beginThread.currentThread().getName()=main this.getName()=Thread-0CountOperate---endrun---beginThread.currentThread().getName()=A this.getName()=Thread-0run---end
因為Thread.currentThread()是當(dāng)前代碼段正在那個線程調(diào)用,CountOperate的構(gòu)造函數(shù)是有main主線程調(diào)用的,run是由Thread線程調(diào)用。同時線程的默認名稱是Thread-(No),這點可以有Thread類的構(gòu)造函數(shù)看出。其中一個構(gòu)造函數(shù)如下:
public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } /* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
重點也就是最后一個this.getName() 為什么是Thread-0?
由上面的Thread構(gòu)造函數(shù)可以看出當(dāng)使用一個Thread對象作為參數(shù)去實例化一個Thread對象時,實現(xiàn)Thread的線程類被緩存進了target對象,而當(dāng)調(diào)用run()方法時,Thread類是這樣實現(xiàn)的
/** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } }
方法isAlive() 的功能是判斷當(dāng)前的線程是否處于活動狀態(tài)。
/** * @program: Demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread{ @Override public void run() { System.out.println("run=" + this.isAlive()); } public static void main(String[] args) { Demo mythread = new Demo(); System.out.println("begin ==" + mythread.isAlive()); mythread.start(); System.out.println("end ==" + mythread.isAlive()); } }
輸出結(jié)果
begin ==falseend ==truerun=true
方法isAlive() 的作用是測試線程是否處于活動狀態(tài)。什么是活動狀態(tài)呢?活動狀態(tài)就是線程已經(jīng)啟動且尚未終止。線程處于正在運行或準備開始運行的狀態(tài),就認為線程是“存活”的。
需要說明一下,如以下代碼:
System.out.println("end ==" + mythread.isAlive());
雖然在上面的示例中打印的值是true,但此值是不確定的。打印true 值是因為mythread線程還未執(zhí)行完畢,所以輸出true。如果代碼更改如下:
public static void main(String[] args) throws InterruptedException { MyThread mythread = new MyThread(); System.out.println("begin ==" + mythread.isAlive()); mythread.start(); Thread.sleep(1000); System.out.println("end ==" + mythread.isAlive()); }
輸出結(jié)果
begin ==falserun=trueend ==false
另外,在使用isAlive() 方法時,如果將線程對象以構(gòu)造參數(shù)的方式傳遞給Thread 對象進行start() 啟動時,運行的結(jié)果和前面示例是有差異的。造成這樣的差異的原因還是來自于Thread.currentThread() 和this 的差異。
/** * @program: CountOperate * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 */public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); System.out.println("this.getName()=" + this.getName()); System.out.println("this.isAlive()=" + this.isAlive()); System.out.println("CountOperate---end"); } @Override public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); System.out.println("this.getName()=" + this.getName()); System.out.println("this.isAlive()=" + this.isAlive()); System.out.println("run---end"); } public static void main(String[] args) { CountOperate c = new CountOperate(); Thread t1 = new Thread(c); t1.setName("A"); t1.start(); } }
輸出結(jié)果
CountOperate---begin Thread.currentThread().getName()=main Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falseCountOperate---end run---begin Thread.currentThread().getName()=A Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falserun---end
另一種方式
/** * @program: CountOperate * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 */public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); System.out.println("this.getName()=" + this.getName()); System.out.println("this.isAlive()=" + this.isAlive()); System.out.println("CountOperate---end"); } @Override public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); System.out.println("this.getName()=" + this.getName()); System.out.println("this.isAlive()=" + this.isAlive()); System.out.println("run---end"); } public static void main(String[] args) { CountOperate c = new CountOperate(); c.start(); } }
輸出結(jié)果
CountOperate---begin Thread.currentThread().getName()=main Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falseCountOperate---end run---begin Thread.currentThread().getName()=Thread-0Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=truerun---end
方法sleep() 的作用是在指定的毫秒數(shù)內(nèi)讓當(dāng)前“正在執(zhí)行的線程”休眠(暫停執(zhí)行)。這個“正在執(zhí)行的線程”是指this.currentThread() 返回的線程。
/** * @program: CountOperate * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 */public class CountOperate extends Thread { @Override public void run() { try { System.out.println("run threadName=" + Thread.currentThread().getName() + " begin"); Thread.sleep(2000); System.out.println("run threadName=" + Thread.currentThread().getName() + " end"); } catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { CountOperate c = new CountOperate(); c.start(); } }
getId() 方法的作用是取得線程的唯一標識
/** * @program: CountOperate * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 */public class CountOperate extends Thread { public static void main(String[] args) { Thread runThread = Thread.currentThread(); System.out.println(runThread.getName() + " " + runThread.getId()); } }# 輸出結(jié)果 main 1
停止一個線程可以使用Thread.stop() 方法,但最好不用它。雖然它確
實可以停止一個正在運行的線程,但是這個方法是不安全的(unsafe),而且是已被棄用作廢的(deprecated),在將來的Java 版本中,這個方法將不可用或不被支持。
大多數(shù)停止一個線程的操作使用Thread.interrupt() 方法,盡管方法的名稱是“停止,中止”的意思,但這個方法不會終止一個正在運行的線程,還需要加入一個判斷才可以完成線程的停止。
在Java 中有以下3 種方法可以終止正在運行的線程:
使用退出標志,使線程正常退出,也就是當(dāng)run 方法完成后線程終止。
使用stop 方法強行終止線程,但是不推薦使用這個方法,因為stop 和suspend 及resume 一樣,都是作廢過期的方法,使用它們可能產(chǎn)生不可預(yù)料的結(jié)果。
使用interrupt 方法中斷線程。
本示例將調(diào)用interrupt() 方法來停止線程, 但interrupt() 方法的使用效果并不像for+break 語句那樣,馬上就停止循環(huán)。調(diào)用interrupt() 方法僅僅是在當(dāng)前線程中打了一個停止的標記,并不是真的停止線程。
/** * @program: MyThread * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 */public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 500000; i++) { System.out.println("i=" + (i + 1)); } } public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); thread.interrupt(); } catch (Exception e) { System.out.println("main catch"); e.printStackTrace(); } } }
輸出結(jié)果
... ... i=499995 i=499996 i=499997 i=499998 i=499999 i=500000
從運行的結(jié)果來看,調(diào)用interrupt 方法并沒有停止線程。
如何停止線程呢,請繼續(xù)向下讀。
在介紹如何停止線程的知識點前,先來看一下如何判斷線程的狀態(tài)是不是停止的。在Java 的SDK 中,Thread.java 類里提供了兩種方法。
this.interrupted():測試當(dāng)前線程是否已經(jīng)中斷。
this.isInterrupted():測試線程是否已經(jīng)中斷。
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread{ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("i=" + (i + 1)); } } public static void main(String[] args) throws InterruptedException { try { Demo thread = new Demo(); thread.start(); thread.interrupt();//Thread.currentThread().interrupt(); System.out.println(" 是否停止1 ? ="+thread.isInterrupted()); System.out.println(" 是否停止2 ? ="+thread.interrupted()); } catch (Exception e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } }
輸出結(jié)果
是否停止1 ? =true 是否停止2 ? =false end! i=1 ... i=45 i=46 i=47 i=48 i=49 i=50
判斷thread 對象所代表的線程是否停止,但從控制臺打印的結(jié)果來看,線程并未停止,這也就證明了interrupted() 方法的解釋:測試當(dāng)前線程是否已經(jīng)中斷。這個“當(dāng)前線程”是main,它從未中斷過,所以打印的結(jié)果是兩個false。
第二種情況
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread{ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("i=" + (i + 1)); } } public static void main(String[] args) throws InterruptedException { try { Demo thread = new Demo(); thread.start(); Thread.sleep(1000); thread.interrupt(); System.out.println(" 是否停止1 ? ="+thread.isInterrupted()); System.out.println(" 是否停止2 ? ="+thread.interrupted()); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } }
輸出結(jié)果
i=1 ... i=47 i=48 i=49 i=50 是否停止1 ? =false 是否停止2 ? =false end!
為什么兩種結(jié)果不一樣呢,因為休息一秒后,線程早已經(jīng)結(jié)束了,所以不能中斷了,interrupted() 方法的解釋:測試當(dāng)前線程是否已經(jīng)中斷。等價于
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread{ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("i=" + (i + 1)); } } public static void main(String[] args) throws InterruptedException { try { Demo thread = new Demo(); thread.start(); Thread.sleep(1000); thread.interrupt(); System.out.println(" 是否停止1 ? ="+thread.isInterrupted()); System.out.println(" 是否停止2 ? ="+Thread.interrupted()); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } }
最后,再來看一下這兩個方法的解釋。
this.interrupted() :測試當(dāng)前線程是否已經(jīng)是中斷狀態(tài),
執(zhí)行后具有將狀態(tài)標志置清除為false 的功能。
this.isInterrupted() :測試線程Thread 對象是否已經(jīng)
是中斷狀態(tài),但不清除狀態(tài)標志。
直接上代碼
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 5000; i++) { if (Thread.interrupted()) { System.out.println(" 已經(jīng)是停止狀態(tài)了! 我要退出了!"); break; } System.out.println("i=" + (i + 1)); } //System.out.println(" 我被輸出,如果此代碼是for 又繼續(xù)運行,線程并未停止! "); } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); thread.interrupt(); System.out.println(" 是否停止1 ? =" + thread.isInterrupted()); System.out.println(" 是否停止2 ? =" + Thread.interrupted()); System.out.println("end!"); } }
輸出結(jié)果
是否停止1 ? =true 是否停止2 ? =falseend! 已經(jīng)是停止狀態(tài)了! 我要退出了!
上面的示例雖然停止了線程,但如果for 語句下面還有語句,還是會繼續(xù)運行的。
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread { @Override public void run() { try { for (int i = 0; i < 500000; i++) { if (Thread.interrupted()) { System.out.println(" 已經(jīng)是停止狀態(tài)了! 我要退出了!"); throw new InterruptedException(); } System.out.println("i=" + (i + 1)); } System.out.println(" 我在for 下面"); } catch (InterruptedException e) { System.out.println(" 進MyThread.java 類run 方法中的catch 了! "); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); thread.interrupt(); System.out.println(" 是否停止1 ? =" + thread.isInterrupted()); System.out.println(" 是否停止2 ? =" + Thread.interrupted()); System.out.println("end!"); } }
輸出結(jié)果
是否停止1 ? =true是否停止2 ? =falseend! 已經(jīng)是停止狀態(tài)了! 我要退出了! 進MyThread.java 類run 方法中的catch 了! java.lang.InterruptedException at Demo.run(Demo.java:14)
如果在sleep狀態(tài)下停止某一線程,會進入catch語句,并且清除停止狀態(tài)值,使之變成false.
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread { @Override public void run() { try { System.out.println("run begin"); Thread.sleep(200000); System.out.println("run end"); } catch (InterruptedException e) { System.out.println("在沉睡中被停止! 進入catch!"+this.isInterrupted()); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { try { Demo thread = new Demo(); thread.start(); Thread.sleep(200); thread.interrupt(); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } }
輸出結(jié)果
run beginend! 在沉睡中被停止! 進入catch!falsejava.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at Demo.run(Demo.java:12)
另一種情況
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread { @Override public void run() { try { for(int i=0;i<100000;i++){ System.out.println("i="+(i+1)); } System.out.println("run begin"); Thread.sleep(200000); System.out.println("run end"); } catch (InterruptedException e) { System.out.println(" 先停止,再遇到了sleep! 進入catch!"); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); thread.interrupt(); System.out.println("end!"); } }
輸出結(jié)果
... i=99998 i=99999 i=100000 run begin 先停止,再遇到了sleep! 進入catch!
使用stop() 方法停止線程則是非常暴力的,調(diào)用stop() 方法時會拋出java.lang.ThreadDeath 異常,但在通常的情況下,此異常不需
要顯式地捕捉,方法stop() 已經(jīng)被作廢,因為如果強
制讓線程停止則有可能使一些清理性的工
作得不到完成。另外一個情況就是對鎖定
的對象進行了“解鎖”,導(dǎo)致數(shù)據(jù)得不到
同步的處理,出現(xiàn)數(shù)據(jù)不一致的問題。
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread { @Override public void run() { try { for(int i=0;i<100000;i++){ System.out.println("i="+(i+1)); Thread.sleep(2000); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); Thread.sleep(10000); thread.stop(); System.out.println("end!"); } }
輸出結(jié)果
i=1 i=2 i=3 i=4 i=5 end!
stop停止導(dǎo)致數(shù)據(jù)不一致
/** * Created by Lee on 2018/7/28. */public class StopSynchronizedObject { private String username = "a"; private String password = "aa"; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } synchronized public void printString(String username, String password) { try { this.username = username; Thread.sleep(8000); this.password = password; } catch (InterruptedException ex) { ex.printStackTrace(); } } }/** * Created by Lee on 2018/7/28. */public class StopSynchronizedThread extends Thread { private StopSynchronizedObject object; public StopSynchronizedThread(StopSynchronizedObject object) { this.object=object; } @Override public void run() { object.printString("b","bb"); } }/** * Created by Lee on 2018/7/28. */public class StopSynchronizedRun { public static void main(String[] args) { try { StopSynchronizedObject object = new StopSynchronizedObject(); StopSynchronizedThread thread = new StopSynchronizedThread(object); thread.start(); Thread.sleep(500); thread.stop(); System.out.println(object.getUsername()); System.out.println(object.getPassword()); } catch (InterruptedException e) { e.printStackTrace(); } } }
輸出結(jié)果
baa
再看一個例子
/** * Created by Lee on 2018/7/28. */public class StopSynchronizedRun { public static void main(String[] args) { try { StopSynchronizedObject object = new StopSynchronizedObject(); StopSynchronizedThread thread = new StopSynchronizedThread(object); thread.start(); //Thread.sleep(500); thread.stop(); System.out.println(object.getUsername()); System.out.println(object.getPassword()); } catch (Exception e) { e.printStackTrace(); } } }
輸出結(jié)果
aaa
/** * @program: demo * @description: java基礎(chǔ)知識測試 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread { @Override public void run() { while (true) { if (this.isInterrupted()) { System.out.println("停止了!"); return; } System.out.println("timer=" + System.currentTimeMillis()); } } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); Thread.sleep(1000); thread.interrupt(); System.out.println("end!"); } }
輸出結(jié)果
timer=1532932687078 timer=1532932687078 end! 停止了!
暫停線程意味著此線程還可以恢復(fù)運行。在Java 多線程中,可以使用suspend() 方法暫停線程,使用resume() 方法恢復(fù)線程的執(zhí)行。
/** * @program: demo * @description: demo * @author: lee * @create: 2018-07-29 11:05 **/public class SuspendThread extends Thread { private long i = 0; public long getI() { return i; } public void setI(long i) { this.i = i; } @Override public void run(){ while (true) { i++; } } } /** * Created by Lee on 2018/7/28. */public class SuspendThreadRun { public static void main(String[] args) { try { SuspendThread thread = new SuspendThread(); thread.start(); Thread.sleep(1000); //第一段 thread.suspend(); System.out.println("I="+thread.getI()); Thread.sleep(5000); System.out.println("I="+thread.getI()); //第二段 thread.resume(); Thread.sleep(1000); //第三段 thread.suspend(); System.out.println("I="+thread.getI()); Thread.sleep(5000); System.out.println("I="+thread.getI()); thread.stop(); } catch (InterruptedException ex) { ex.printStackTrace(); } } }
輸出結(jié)果
I=483701768I=483701768I=1001747085I=1001747085
在使用suspend 與resume 方法時,如果使用不當(dāng),極易造成公共的同步對象的獨占,使得其他線程無法訪問公共同步對象。
public class SynchronizedObject { synchronized public void printString() { System.out.println("begin"); if (Thread.currentThread().getName().equals("a")) { System.out.println("a 線程永遠 suspend 了! "); Thread.currentThread().suspend(); } System.out.println("end"); } }public class Run { public static void main(String[] args) { try { final SynchronizedObject object = new SynchronizedObject(); Thread thread1 = new Thread() { @Override public void run() { object.printString(); } }; thread1.setName("a"); thread1.start(); Thread.sleep(1000); Thread thread2 = new Thread() { @Override public void run() { System.out.println("thread2 啟動了,但進入不了printString() 方法! 只打印1 個begin"); System.out.println(" 因為printString() 方法被a 線程鎖定并且永遠 suspend 暫停了! "); object.printString(); } }; thread2.start(); } catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace(); } } }
還有另外一種獨占鎖的情況也要格外注意,稍有不慎,就會掉進“坑”里
public class MyThread extends Thread { private long i = 0; @Override public void run() { while (true) { i++; } } } public class Run { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(1000); thread.suspend(); System.out.println("main end!"); } catch (InterruptedException e) { e.printStackTrace(); } } }
輸出結(jié)果
main end!
如果更改程序如下
/** * @program: demo * @description: 2 * @author: lee * @create: 2018-07-30 14:52 **/public class test extends Thread{ private long i = 0; @Override public void run() { while (true) { i++; System.out.println(i); } } public static void main(String[] args) { try { test thread = new test(); thread.start(); Thread.sleep(1000); thread.suspend(); System.out.println("main end!"); } catch (InterruptedException e) { e.printStackTrace(); } } }
輸出結(jié)果
... 113720 113721 113722 113723
不再會有main end!輸出,出現(xiàn)這樣情況的原因是,當(dāng)程序運行到println() 方法內(nèi)部停止時,同步鎖未被釋放。
/** * Prints a String and then terminate the line. This method behaves as * though it invokes <code>{@link #print(String)}</code> and then * <code>{@link #println()}</code>. * * @param x The <code>String</code> to be printed. */ public void println(String x) { synchronized (this) { print(x); newLine(); } }
在使用suspend 與resume 方法時也容易出現(xiàn)因為線程的暫停而導(dǎo)致數(shù)據(jù)不同步的情況。
public class MyObject { private String username = "1"; private String password = "11"; public void setValue(String u, String p) { this.username = u; if (Thread.currentThread().getName().equals("a")) { System.out.println(" 停止a 線程! "); Thread.currentThread().suspend(); } this.password = p; } public void printUsernamePassword() { System.out.println(username + " " + password); } }public class Run { public static void main(String[] args) throws InterruptedException { final MyObject myobject = new MyObject(); Thread thread1 = new Thread() { public void run() { myobject.setValue("a", "aa"); }; }; thread1.setName("a"); thread1.start(); Thread.sleep(500); Thread thread2 = new Thread() { public void run() { myobject.printUsernamePassword(); }; }; thread2.start(); } }
輸出結(jié)果
停止a線程 a 11
yield() 方法的作用是放棄當(dāng)前的CPU 資源,將它讓給其他的任務(wù)去占用CPU 執(zhí)行時間。但放棄的時間不確定,有可能剛剛放棄,馬上又獲得CPU 時間片。
/** * @program: demo * @description: demo * @author: lee * @create: 2018-07-29 11:21 **/public class YieldThread extends Thread { @Override public void run(){ long beginTime = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 5000000; i++) { // Thread.yield(); count = count + (i+1); } long endTime=System.currentTimeMillis(); System.out.println("用時:" + (endTime-beginTime) + "毫秒!"); } public static void main(String[] args) { YieldThread thread = new YieldThread(); thread.start(); } } # 輸出結(jié)果 用時:2毫秒!# 沒有注釋Thread.yield();輸出結(jié)果 用時:1809毫秒!
在操作系統(tǒng)中,線程可以劃分優(yōu)先級,優(yōu)先級較高的線程得到的CPU 資源較多,也就是CPU 優(yōu)先執(zhí)行優(yōu)先級較高的線程對象中的任務(wù),在Java 中,線程的優(yōu)先級分為1 ~ 10 這10 個等級,如果小于1 或大于10,則JDK 拋出異常throw new IllegalArgumentException()。
JDK 中使用3 個常量來預(yù)置定義優(yōu)先級的值,代碼如下:
public final static int MIN_PRIORITY = 1;public final static int NORM_PRIORITY = 5;public final static int MAX_PRIORITY = 10;
在Java 中,線程的優(yōu)先級具有繼承性,比如A 線程啟動B 線程,則B 線程的優(yōu)先級與A 是一樣的。
/** * @program: demo * @description: MyThread1 * @author: lee * @create: 2018-07-30 15:38 **/public class MyThread1 extends Thread { @Override public void run() { System.out.println("MyThread1 run priority=" + this.getPriority()); MyThread2 thread2 = new MyThread2(); thread2.start(); } }/** * @program: demo * @description: MyThread2 * @author: lee * @create: 2018-07-30 15:38 **/public class MyThread2 extends Thread { @Override public void run() { System.out.println("MyThread2 run priority=" + this.getPriority()); } }/** * @program: demo * @description: demo * @author: lee * @create: 2018-07-30 15:39 **/public class RunDemo { public static void main(String[] args) { MyThread1 thread = new MyThread1(); thread.start(); } }
輸出結(jié)果
MyThread1 run priority=5MyThread2 run priority=5
雖然使用setPriority() 方法可以設(shè)置線程的優(yōu)先級,但還沒有看到設(shè)置優(yōu)先級所帶來的效果。
import java.util.Random;/** * @program: demo * @description: 線程優(yōu)先級測試 * @author: lee * @create: 2018-07-29 11:37 **/public class PropertyThread1 extends Thread{ @Override public void run() { long beginTime = System.currentTimeMillis(); long addResult = 0; for (int j = 0;j < 100; j++) { Random random = new Random(); random.nextInt(); addResult = addResult +j; long endTime = System.currentTimeMillis(); System.out.println("*************** PropertyThread1 use time= " +(endTime-beginTime)); } } }import java.util.Random;/** * @program: demo * @description: 線程優(yōu)先級測試 * @author: lee * @create: 2018-07-29 11:37 **/public class PropertyThread2 extends Thread{ @Override public void run() { long beginTime = System.currentTimeMillis(); long addResult = 0; for (int j = 0;j < 100; j++) { Random random = new Random(); random.nextInt(); addResult = addResult +j; long endTime = System.currentTimeMillis(); System.out.println("######### PropertyThread2 use time= " +(endTime-beginTime)); } } }/** * @program: demo * @description: property測試 * @author: lee * @create: 2018-07-29 11:43 **/public class PropertyRun { public static void main(String[] args) { PropertyThread1 thread1 = new PropertyThread1(); thread1.setPriority(2); thread1.start(); PropertyThread2 thread2 = new PropertyThread2(); thread2.start(); thread2.setPriority(6); } }
輸出結(jié)果
...######### PropertyThread2 use time= 1*************** PropertyThread1 use time= 2######### PropertyThread2 use time= 2*************** PropertyThread1 use time= 3######### PropertyThread2 use time= 2...*************** PropertyThread1 use time= 11*************** PropertyThread1 use time= 11*************** PropertyThread1 use time= 11*************** PropertyThread1 use time= 11*************** PropertyThread1 use time= 11
高優(yōu)先級的線程總是大部分先執(zhí)行完,但不代表高優(yōu)先級的線
程全部先執(zhí)行完。另外,不要以為thread1 線程先被main 線程所調(diào)用就會先執(zhí)行完,當(dāng)線程優(yōu)先級的等級差距很大時,誰先執(zhí)行完和代碼的調(diào)用順序無關(guān),線程的優(yōu)先級具有一定的規(guī)則性,也就是CPU 盡量將執(zhí)行資源讓給優(yōu)先級比較高的線程。
線程的優(yōu)先級還具有“隨機性”,也就是優(yōu)先級較高的線程不一定每一次
都先執(zhí)行完,當(dāng)優(yōu)先級的差距不大的時候最能體現(xiàn)出隨機性。
線程的優(yōu)先級越高,CPU分配得到的機會越大,所以執(zhí)行的時間越多。
在Java 線程中有兩種線程,一種是用戶線程,另一種是守護線程。
守護線程是一種特殊的線程,它的特性有“陪伴”的含義,當(dāng)進程中不存在非守護線程了,則守護線程自動銷毀。典型的守護線程就是垃圾回收線程,當(dāng)進程中沒有非守護線程了,則垃圾回收線程也就沒有存在的必要了,自動銷毀。
用個比較通俗的比喻來解釋一下
“守護線程”:任何一個守護線程都是整個JVM 中所有非守護線程的“保姆”,只要當(dāng)前JVM
實例中存在任何一個非守護線程沒有結(jié)束,守護線程就在工作,只有當(dāng)最后一個非守護線程
結(jié)束時,守護線程才隨著JVM 一同結(jié)束工作。
注意點:
thread.setDaemon(true)必須在thread.start()之前設(shè)置。 在Daemon線程中產(chǎn)生的新線程也是Daemon的。 不是所有的應(yīng)用都可以分配給Daemon線程來進行服務(wù),比如讀寫操作或者計算邏輯。因為Daemon Thread還沒來得及進行操作,虛擬機可能已經(jīng)退出了。
/** * @program: demo * @description: 守護線程 * @author: lee * @create: 2018-07-29 12:04 **/public class DaemonThread extends Thread{ private int i = 0; @Override public void run() { while (true) { try { System.out.println(i++); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }/** * @program: demo * @description: 守護進程 * @author: lee * @create: 2018-07-29 12:06 **/public class DaemonRun { public static void main(String[] args) throws InterruptedException { DaemonThread thread = new DaemonThread(); thread.setDaemon(true); thread.start(); Thread.sleep(5000); System.out.println("***************************************************************再也不會打印東西了。。。。"); } }
輸出結(jié)果
0 1 2 3 4***************************************************************再也不會打印東西了。。。。
如果多線程類改變一下
/** * @program: demo * @description: 守護線程 * @author: lee * @create: 2018-07-29 12:04 **/public class DaemonThread extends Thread{ private int i = 0; @Override public void run() { while (true) { System.out.println(i++); } } }
輸出結(jié)果
... 106250 106251 106252***************************************************************再也不會打印東西了。。。。 106253 106254 106255 ... 106341 Process finished with exit code 0