線程的正確中止

線程中止的幾種方式:

  1. 線程運行完成,正常中止java

  2. 執行stop方法,暴力中止,現已棄用數據庫

  3. 執行interrupt方法,如今使用。ide

stop方法爲何被棄用?

使用stop方法終止線程會釋放掉此線程鎖定的全部的監視器,若是線程修改了鎖定對象的內容在尚未被正常處理以前線程被終止了。將會形成數據不一致的後果。函數

例如銀行取款的例子:在線程A中進行取款操做,取款操做是一個同步方法,其中共有三個步驟:輸入密碼,取錢,修改餘額。當用戶a在輸入密碼,取錢以後,線程A.stop();線程終止。被修改的餘額尚未寫回數據庫,從而形成數據混亂。測試

舉例說明:首先定義一個操做類Loginthis

package com.feng.example;

public class Login {
	private String username="a";
	private String password="aa";
	
	
	synchronized public String getUsername() {
		return username;
	}
	
	public void setUsername(String username) { 
		this.username = username;
	}
	
	synchronized public String getPassword() {
		return password;
	}
	
	public void setPassword(String password) {
		this.password = password;
	}
	
	synchronized public void login(String username, String passwd)
	{
		this.username = username;
		try {
			Thread.sleep(20000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.password = passwd;
	}
	

}

定義線程類,調用login方法spa

package com.feng.example;

public class LoginThread extends Thread{

	private Login login;
	
	public LoginThread(Login login)
	{
		this.login = login;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		login.login("b", "bb");
		
	}

}

測試類:線程

package com.feng.example;

public class LoginTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Login login = new Login();
		Thread thread = new LoginThread(login);
		thread.start();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		thread.stop();  //強制終止線程
		
		System.out.println(login.getUsername()+"========"+login.getPassword());
		

	}

}

分析:login對象的username初值爲a,password初值爲aa,線程thread調用login("b","bb");方法,將username的值修改成b,此時線程休眠20秒,在線程thread start以後2秒調用thread.stop,線程終止。釋放掉對login對象的鎖。只有釋放掉對login的鎖才能調用同步方法getUsername和getPassword.code

程序運行結果:對象

b========aa

正確的線程中止方式

使用interrupt方法,此方法只是給線程一個標記,表示此線程要被終止(只是一個標記,沒有作出任何終止線程的行爲),經過配合使用interrupted方法或者isInterrupted方法來檢測線程是否要中止,若是檢測到要中止,則先進行完相應的操做以後使用break或者return正常執行完(在有循環體的狀況下)或者使用拋出異常的方式終止線程(推薦使用)

經過上述內容能夠看出正確中止線程的思想就是:利用某種手段使線程正常中止或者拋出異常的方式終止線程

1.介紹interrupted和isInterrupted方法

interrupted方法 靜態方法  檢測當前線程(執行這個方法的線程)是否被中斷(判斷有沒有中斷標記)

isInterrupted方法  檢測調用者是否被中斷

測試interrupted方法的使用,建立MyThread線程類

package com.feng.example;

public class MyThread extends Thread {
	
	@Override
	public void run() {
		int i = 0;
		while(true)
		{
			i++;
		}

	}	
}

測試類:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		Thread thread = new MyThread();
		thread.start();
		thread.interrupt();
		System.out.println(thread.interrupted()+"=========="+Thread.interrupted()); //main函數執行的這段代碼,因此當前線程是main
		System.out.println(thread.interrupted()+"=========="+Thread.interrupted());
		
	}

}

分析:在main函數中啓動thread,而後使用thread.interrupt()終止線程(其實就是打一個終止標記),而輸出語句中使用的是interrupted方法,此方法檢測的是當前線程,即執行這句話的線程也就是main,由於終止的是thread而不是main,因此打印出來的結果都是false。

這裏由於interrupted是靜態方法,使用thread.interrupted() 和Thread.interrupted()是同樣的。

測試結果:

false==========false
false==========false

修改上面的測試程序,使主線程終止,修改程序以下:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		Thread thread = new MyThread();
		thread.start();
		Thread.currentThread().interrupt();
		System.out.println(thread.interrupted()+"=========="+Thread.interrupted()); //main函數執行的這段代碼,因此當前線程是main
		System.out.println(thread.interrupted()+"=========="+Thread.interrupted());
		
	}

}

分析:如今終止的是主線程,所以在輸出的時候調用thread.interrupted()輸出true。由於interrupted方法具備清除中斷標記的功能,所以再次調用interrupted()方法時,輸出false

測試若是以下:

true==========false
false==========false

修改測試程序查看isInterrupted方法的使用:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		Thread thread = new MyThread();
		thread.start();
		thread.interrupt();
		System.out.println(thread.isInterrupted()+"=========="+thread.isInterrupted()); //main函數執行的這段代碼,因此當前線程是main
		
	}

}

分析:isInterrupted方法不具備清除中斷標記的做用,所以兩次輸出都爲true

運行結果:

true==========true

2.使用break跳出循環,是線程運行完畢

修改線程類

package com.feng.example;

public class MyThread extends Thread {
	
	@Override
	public void run() {
		int i = 0;
		while(true)
		{
			if(this.interrupted())  //這裏換作this.isInterrupted()也能夠
			{
				//作相應的處理
				System.out.println("線程正常終止");
				break;
			}
			i++;
		}

	}	
}

測試類代碼:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		Thread thread = new MyThread();
		thread.start();
		thread.interrupt();
		
	}

}

當在while(true)循環中判斷若是又中斷則跳出循環,今後處也能夠看到thread.interrupt();只是作一箇中斷標記,當線程檢測到這個中斷標記後,能夠作一些必要的操做後跳出循環,正常運行完線程。

運行結果:

線程正常終止

3.使用return退出線程

若是上述的線程中有多個循環,break就只能跳出一個循環,從而進入另外一個循環中,仍是終止不了線程。咱們可使用return來退出循環。

先看問題:修改上述的線程類

package com.feng.example;

public class MyThread extends Thread {
	
	@Override
	public void run() {
		
		for(int i=0; i<1000; i++)
		{
			if(this.isInterrupted())
			{
				//作相應的處理
				System.out.println("線程正常終止");
				break;
			}
			i++;
		}
		
		System.out.println("線程還能夠運行,若是我是一個循環又會進入一個循環");

	}	
}

查看執行結果:

線程正常終止
線程還能夠運行,若是我是一個循環又會進入一個循環

說明使用break的話,循環外的語句仍是會被執行的,不想讓循環外的語句執行就換爲return

修改線程類代碼:

package com.feng.example;

public class MyThread extends Thread {
	
	@Override
	public void run() {
		
		for(int i=0; i<1000; i++)
		{
			if(this.isInterrupted())
			{
				//作相應的處理
				System.out.println("線程正常終止");
				return;
			}
			i++;
		}
		
		System.out.println("線程還能夠運行,若是我是一個循環又會進入一個循環");

	}	
}

運行結果以下:

線程正常終止

return通常會對代碼形成污染,所以咱們仍是建議使用拋出異常的方法,來終止線程

4.拋出異常,終止線程

修改線程類

package com.feng.example;

public class MyThread extends Thread {

	@Override
	public void run() {

		try {
			for (int i = 0; i < 1000; i++) {
				if (this.isInterrupted()) {
					// 作相應的處理
					System.out.println("線程正常終止");
					throw new InterruptedException();
				}
				i++;
			}
			
			System.out.println("線程還能夠運行,若是我是一個循環又會進入一個循環");
			
		} catch (InterruptedException e) {
			System.out.println("進入Catch");
			e.printStackTrace();
		}

	}
}

運行結果:

線程正常終止
進入Catch
java.lang.InterruptedException
	at com.feng.example.MyThread.run(MyThread.java:13)

interrupt睡眠中的線程

1.線程先進入sleep,而後再interrupt

定義線程類:

package com.feng.example;

public class MyThread extends Thread {

	@Override
	public void run() {

		try {
			System.out.println("線程執行");
			Thread.sleep(10000);
			System.out.println("線程執行完成");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  //休眠10秒
		
	}
}

測試類:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		Thread thread = new MyThread();
		thread.start();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		thread.interrupt();
		
	}

}

終止sleep中的線程,會拋出interruptedException

運行結果:

線程執行
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.feng.example.MyThread.run(MyThread.java:10)

2.先interrupted,線程再進入sleep

修改線程類:

package com.feng.example;

public class MyThread extends Thread {

	@Override
	public void run() {

		try {
			//消耗一下時間,
			for(int i=0; i<10000; i++)
			{
				System.out.println(i);
			}
			System.out.println("線程執行");
			Thread.sleep(10000);
			System.out.println("線程執行完成");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			System.out.println("進入Catch");
			e.printStackTrace();
		}  //休眠10秒
		
	}
}

測試類:

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		Thread thread = new MyThread();
		thread.start();
		thread.interrupt();
		System.out.println("end");
		
	}

}

運行結果:

9997
9998
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.feng.example.MyThread.run(MyThread.java:15)
9999
線程執行
進入Catch

因而可知:先sleep再interrupt會直接拋出異常,若是先interrupt,再進入sleep,在sleep時纔會拋出異常

相關文章
相關標籤/搜索