關於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,鄭州不孕不育好醫院:http://www.zzchyy110.com/
將方法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)這句創建的線程