停止線程和暫停線程
停止一個線程意味着在線程處理完任務之前停止正在做的操作,也就是放棄當前的操作,雖然看起來非常簡單,但是要做好防範措施。
停止線程的方法
1. 方法1:使用Thread.stop(),但不推薦
2. 方法2:使用Thread.interrupt()
2.1 使用實例:
創建文件MyThread.java:
public class MyThread extends Thread{
@Override
public void run() {
super.run();
for (int i = 0;i < 50000 ; i++){
System.out.println("i="+i);
}
}
}
創建測試類RunTest.java:
public class RunTest {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
System.out.println("ZZZZZZZZZ");
}
}
預期結果:輸出50000行+ZZZZZ,實際輸出50001行+zzzz
這說明了什麼?調用interrupt()方法並沒有真正的把線程停止。
2.2 判斷線程是否處於停止狀態
2.2.1 public static boolean interrupted():測試currentThread()是否已經中斷,執行後具有清除狀態標誌值爲false的功能,如:第一次調用時,當前線程未中斷,清除該標誌在第二次調用檢驗中斷狀態之前,第二次的調用結果就爲了true。
2.2.2 public boolean this.isInterrupted():測試this關鍵字所在類的對象是否已經中斷,不清楚狀態標誌。
3 .判斷線程是否處於停止狀態即可判斷後面的代碼是否可運行,如果線程處於停止狀態,則後面的代碼不再運行。
3.1 能停止的線程------>異常法,如果結果不明顯,可配合斷點一起使用便於觀察結果
修改MyThread.java如下:
public class MyThread extends Thread{
@Override
public void run() {
super.run();
for (int i = 0;i < 50000 ; i++){
if(this.isInterrupted()){
System.out.println("我已經是停止狀態了,我要退出了");
break;
}
System.out.println("i="+i);
}
}
}
修改RunTest.java如下:
public class RunTest {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(5000);
thread.interrupt();
}catch (InterruptedException e){
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
實際的運行結果:
i=0
i=1
end!
我已經是停止狀態了,我要退出了
3.2 在3.1中的例子雖然停止了線程,但如果for語句下面還有語句,那麼程序依然會繼續運行,如圖:
類MyThread修改如下:
public class MyThread extends Thread{
@Override
public void run() {
super.run();
for (int i = 0;i < 50000 ; i++){
if(this.isInterrupted()){
System.out.println("我已經是停止狀態了,我要退出了");
break;
}
System.out.println("i="+i);
}
System.out.println("我被輸出了,是for下的語句,線程結束後我還能執行嘿!");
}
}
實際運行結果如下,說明並沒有阻止for後的語句再繼續執行
i=0
i=1
i=2
end!
我已經是停止狀態了,我要退出了
我被輸出了,是for下的語句,線程結束後我還能執行嘿!
3.2.1 解決辦法,即使用interrupt()正確停止線程的方法
修改MyThread類如下:
public class MyThread extends Thread{
@Override
public void run() {
super.run();
try {
for (int i = 0;i < 50000 ; i++){
if(this.isInterrupted()){
System.out.println("我已經是停止狀態了,我要退出了");
throw new InterruptedException();
}
System.out.println("i="+i);
}
System.out.println("我被輸出了,是for下的語句!");
}catch (InterruptedException e){
System.out.println("進入了MyThread類的run方法中的catch了");
e.printStackTrace();
}
}
}
實際的運行結果部分如下:
i=14006
i=14007
i=14008
i=14009
我已經是停止狀態了,我要退出了
end!
進入了MyThread類的run方法中的catch了
java.lang.InterruptedException
at MyThread.run(MyThread.java:10)
4.使用“return;”語句和interrupt()方法結合使用停止線程的缺點和解決方案
4.1 修改MyThread類如下:
public class MyThread extends Thread{
@Override
public void run() {
super.run();
while(true){
if(this.isInterrupted()){
System.out.println("停止了哦!");
return;
}
System.out.println("timer:"+System.currentTimeMillis());
}
}
}
4.2 修改RunTest類如下:
public class RunTest {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
最後的實際運行結果爲,證明停止了之後,沒有再繼續往下執行
timer:1592300686447
timer:1592300686447
timer:1592300686447
timer:1592300686447
停止了哦!
4.3 使用return的缺點是:不能像拋異常法那樣對異常的信息進行統一的處理,解決辦法:只能在每個return前都要搭配一個寫入日誌的操作,所以這會使得代碼冗餘,所以還是建議使用拋異常的方法來停止線程。
5. 暫停線程
在Java多線程中,可以使用suspend()來暫停線程,resume()來恢復線程的執行。
簡單使用的示例:
public class MyThread extends Thread{
private long i = 0;
public long getI(){
return i;
}
public void setI(long i){
this.i = i;
}
@Override
public void run() {
super.run();
while(true){
i++;
}
}
}
我的RunTest類如下:
public class RunTest {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(5000);
//A段
thread.suspend();//此方法已過期,但是研究過期方法爲什麼被丟棄也是件有趣的事情
System.out.println("A="+System.currentTimeMillis()+",i="+thread.getI());
Thread.sleep(5000);
System.out.println("A="+System.currentTimeMillis()+",i="+thread.getI());
//B段
thread.resume();
Thread.sleep(5000);
//C段
thread.suspend();
System.out.println("B="+System.currentTimeMillis()+",i="+thread.getI());
Thread.sleep(5000);
System.out.println("B="+System.currentTimeMillis()+",i="+thread.getI());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
最後的運行結果爲:
A=1592302312763,i=2823139737
A=1592302317763,i=2823139737
B=1592302322763,i=5736528459 ----》此時線程已被暫停,不再執行run方法的i++
B=1592302327763,i=5736528459 ----->已被暫停的線程,未被恢復,所以i值不變的
5.1 suspend()方法與resume()方法的缺點——————獨佔資源
如果suspend()方法與resume()方法使用不當,極易造成公共同步對象被佔,其他線程無法訪問公共對象的結果。
5.1.1 創建模擬公共對象類:
public class SynchronizedObject {
synchronized public void printString(){
System.out.println("begin");
if(Thread.currentThread().getName().equals("a")){
System.out.println("a線程永遠被暫停了");
Thread.currentThread().suspend();
}
System.out.println("end!");
}
}
5.1.2 創建RunTest類:
public class RunTest {
public static void main(String[] args) {
try {
final SynchronizedObject obj = new SynchronizedObject();
Thread thread1 = new Thread(){
@Override
public void run() {
obj.printString();
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(1000);
Thread thread2 = new Thread(){
@Override
public void run() {
System.out.println("Thread2啓動了,但卻進入不了printString,無法打印出b的begin");
obj.printString();
}
};
thread2.setName("b");
thread2.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
實際運行結果,A線程獨佔並鎖死了公共資源printString()方法
begin
a線程永遠被暫停了
Thread2啓動了,但卻進入不了printString,無法打印出b的begin
5.2 另一種獨佔資源鎖死的情況
5.2.1 創建MyThread類:
public class MyThread extends Thread{
private long i = 0;
@Override
public void run() {
super.run();
while(true){
i++;
}
}
}
5.2.2 創建RunTest類:
public class RunTest {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.suspend();
System.out.println("mainEnd!");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
運行結果如下:輸出mainEnd,但是可以看到進程未被銷燬,在控制檯看到是呈紅色按鈕。
但是如果將MyThread.java更改成如下:
public class MyThread extends Thread{
private long i = 0;
@Override
public void run() {
super.run();
while(true){
i++;
System.out.println(i);
}
}
}
運行結果如下:未輸出mainEnd
144067
144068
144069
144070
144071
144072
144073
144074
產生這種情況的原因是:當程序運行到System.out.prtintln(i)內部時,同步鎖是不釋放的,println()的源代碼如下:
public void println(long x){
synchronized (this){
print(x);
newLine();
}
}
以上便是多線程的暫停和徹底停止的內容,關於suspend()和resume()一起使用還可能造成數據的不完整的缺點這裏就不再述說,現在JDK版本已經將這兩個方法作廢了,想要對線程進行暫停與恢復的處理,可使用wait(),notify()或notifyAll()方法。