來源: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