深入理解Java多執行緒(一)
關於java多執行緒的概念以及基本用法:java多執行緒基礎
1,停止執行緒
停止執行緒意味著線上程執行完之前停止正在做的操作,即立刻放棄當前的操作,這並不容易。停止執行緒可以用Thread.stop()方法,但是這個方法不安全,所以不建議使用,還有一個方法就是Thread.interrupt()方法,但是這個方法不會終止一個正在執行的執行緒,需要新增一個判斷才可以完成執行緒的停止
1.1,停不下來的執行緒
- public void interrupt()
將呼叫者執行緒的中斷狀態設為true
Thread.interrupt()方法只是在當前執行緒中打了一個停止的標記,並不是直接將執行緒停止
class MyThread_1 extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<10000;i++) {
System.out.println("i="+(i+1));
}
}
}
public class Demo1_7_1 {
public static void main(String[] args) {
try {
MyThread_1 thread = new MyThread_1();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
}
}
結果:
從結果可以看到,使用thread.interrupt()執行緒並未停止
1.2,判斷執行緒是否是停止狀態
- public static boolean interrupted
只能通過Thread.interrupted()呼叫
class MyThread_2 extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<10000;i++) {
System.out.println("i="+(i+1));
}
}
}
public class Demo1_7_2 {
public static void main(String[] args) {
try {
MyThread_2 thread = new MyThread_2();
thread.start();
Thread.sleep(2000);
thread.interrupt();
System.out.println("是否停止1?="+thread.interrupted());
System.out.println("是否停止2?="+thread.interrupted());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("end");
}
}
執行結果:
當前執行緒是main並沒有中斷過,所以列印結果都是false
將main函式程式碼改為
Thread.currentThread().interrupt();
System.out.println("是否停止1?="+Thread.interrupted());
System.out.println("是否停止2?="+Thread.interrupted());
System.out.println("end");
結果為
是否停止1?=true
是否停止2?=false
end
為什麼第二個值是false?這是因為第一次呼叫Thread.interrupted()返回當前執行緒中斷狀態,然後會將執行緒的中斷狀態設為false
- public boolean isInterrupted()
判斷呼叫者執行緒的中斷狀態
講main函式修改
try {
MyThread_2 thread = new MyThread_2();
thread.start();
Thread.sleep(2000);
thread.interrupt();
System.out.println("是否停止1?="+thread.isInterrupted());
System.out.println("是否停止2?="+thread.isInterrupted());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("end");
執行結果是:
。。。
。。。。
i=9997
i=9998
i=9999
i=10000
是否停止1?=false
是否停止2?=false
end
很神奇這裡返回結果都是false,想了半天,後來在網上查了資料明白是sleep函式的原因Thread.sleep(2000);休眠的是主執行緒,這裡休眠的就是main函式,在這段時間裡thread可能已經執行完了,所以thread.interrupt();也就起不到作用了,修改sleep函式時間,返回結果就都是true了
此方法作用:測試執行緒是否已經是中斷狀態,但是不會執行一次後將中斷狀態設定為false
1.3,停止執行緒–異常法
使用if語句判斷執行緒是否是停止狀態來控制後面的程式碼是否繼續執行,但是無論是否停止執行緒,for迴圈外run方法裡的程式碼都會執行,這裡用throw new InterruptedException();來產生異常,從而結束執行緒,執行緒其餘程式碼也不會執行了
class MyThread_3 extends Thread{
@Override
public void run() {
super.run();
try {
for(int i=0;i<500000;i++) {
if(this.isInterrupted()) {
System.out.println("已經是停止狀態了,我要退出");
throw new InterruptedException();
}
System.out.println("i="+(i+1));
}
System.out.println("for下面");
}catch(InterruptedException e) {
System.out.println("進Mythread類run方法中的catch");
e.printStackTrace();
}
}
}
public class Demo1_7_3 {
public static void main(String[] args) {
try {
MyThread_3 thread = new MyThread_3();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("end");
}
}
結果:
。。。。
。。。。。
i=366068
i=366069
i=366070
end
已經是停止狀態了,我要退出
進Mythread類run方法中的catch
java.lang.InterruptedException
at chapter_1.MyThread_3.run(Demo1_7_3.java:12)
1.4,停止執行緒–在Sleep狀態
在sleep狀態下停止某一執行緒會進入catch語句,並且清除停止狀態值,使之變成false
class MyThread_4 extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (Exception e) {
// TODO: handle exception
System.out.println("在沉睡中被停止進入catch"+this.isInterrupted());
e.printStackTrace();
}
}
}
public class Demo1_7_4 {
public static void main(String[] args) {
try {
MyThread_4 thread = new MyThread_4();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (Exception e) {
// TODO: handle exception
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
結果:
run begin
end
在沉睡中被停止進入catchfalse
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at chapter_1.MyThread_4.run(Demo1_7_4.java:10)
1.5,停止執行緒–暴力停止
所謂暴力停止就是最直接的方法了–stop(),這種方法直接讓執行緒停止,但是這也會引來一些問題,比如執行緒的安全問題,不建議使用
1.6,停止執行緒–使用return
將方法interrupt()與return結合使用也可以實現停止執行緒
class MyThread_8 extends Thread{
@Override
public void run() {
super.run();
while(true) {
if(this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("timer="+System.currentTimeMillis());
}
}
}
public class Demo1_7_8 {
public static void main(String[] args) throws InterruptedException{
MyThread_8 t = new MyThread_8();
t.start();
Thread.sleep(2000);
t.interrupt();
}
}
結果:
。。。
。。。
timer=1533907137644
timer=1533907137644
timer=1533907137644
timer=1533907137644
timer=1533907137644
timer=1533907137644
停止了!
1.7,yield方法
yield()方法作用是放棄當前CPU資源,讓給其他執行緒去使用,但是放棄時間不確定
1.8,執行緒優先順序
作業系統會對執行緒進行劃分優先順序,優先順序高的執行緒會優先分配到資源,類似於現實中的VIP,Java將執行緒從1到10劃分十個等級,預設優先順序為NORM_PRIORITY,設定優先順序用setPriority()
setPriority()原始碼:
public final void setPriority(int i)
{
checkAccess();
if(i > 10 || i < 1)
throw new IllegalArgumentException();
ThreadGroup threadgroup;
if((threadgroup = getThreadGroup()) != null)
{
if(i > threadgroup.getMaxPriority())
i = threadgroup.getMaxPriority();
setPriority0(priority = i);
}
}
- public final static int MIN_PRIORITY=1;
- public final static int NORM_PRIORITY=5;
- public final static int MAX_PRIORITY=10;
此外執行緒還具有繼承性,若執行緒A啟動執行緒B,則B具有A的優先順序
1.9,守護執行緒
在Java執行緒中有兩種執行緒,一種是使用者執行緒一種是守護執行緒,我們常說的作業系統中並沒有守護執行緒這一說,原因是Java是構建在JVM之上的。顧名思義,守護執行緒具有陪伴的意思,當程序中不存在非守護執行緒時,守護執行緒就會自動銷燬。
Daemon作用是為了其他執行緒的執行提供便利服務,只有當最後一個非守護執行緒結束時,守護執行緒才會隨著JVM一同結束工作,守護執行緒典型的例子就是GC
class MyThread_11 extends Thread{
private int i=0;
@Override
public void run() {
super.run();
try {
while(true) {
i++;
System.out.println("i="+(i));
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Demo1_11 {
public static void main(String[] args) {
try {
MyThread_11 thread = new MyThread_11();
thread.setDaemon(true);
thread.start();
Thread.sleep(5000);
System.out.println("守護執行緒離開了!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
結果:
i=1
i=2
i=3
i=4
i=5
守護執行緒離開了!
2018.9.9補充:
突然對this與Thread.currentThread()產生了疑問,下面來對這個區別來介紹一下:
建立一個MyObject類:
public class MyThread extends Thread{
public MyThread() {
}
@Override
public void run() {
System.out.println("Thread.currentThread.getName= "
+Thread.currentThread().getName());
System.out.println("this.getName= "+this.getName());
}
}
測試類:
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread t = new Thread(myThread);
t.start();
}
}
結果:
Thread.currentThread.getName= Thread-1
this.getName= Thread-0
顯然這裡this和Thread.currentThread()指向的不是同一個執行緒,看一下Thread原始碼:
Thread t = new Thread(myThread)這句傳入的物件MyThread就是原始碼裡的target物件,Thread類的構造方法會給執行緒一個預設的名字,從”Thread-0”開始
this.getName()其實就是target.getName(),返回的是執行MyThread myThread = new MyThread()的執行緒名,而Thread.currentThread().getName()返回的是t.getName(),返回的是Thread t = new Thread(myThread)這句建立的執行緒