線程中斷,stop() 和 interrupt()

正常來說線程裏的代碼執行完之後線程就自動中斷了,但是一些處於無線循環當中的線程需要另外通過程序進行中斷。

stop(),顧名思義就是停止線程,但是當前這個api已經被廢棄,不建議使用。

原因是調用 stop() 會立即中斷線程,無論線程執行到了哪裏都立即停止,並且釋放其佔有的鎖。這對於線程執行的完整性造成了破壞。

例如下面這個例子

package demo1;

public class StopUnSafeDemo {
	
	public static User u = new User();
	
	public static class User {
		
		private int id;
		
		private String name;

		public int getId() {
			return id;
		}

		public void setId(int id) {
			this.id = id;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}
		
		public User() {
			id = 0;
			name = "0";
		}

		@Override
		public String toString() {
			return "User [id=" + id + ", name=" + name + "]";
		}
		
	
	}
	
	public static class ChangeObjectThread extends Thread {
		
		@Override
		public void run() {
			while(true) {
				synchronized(u) { //設置 id 和 name,將其設置爲一樣的值
					int v = (int)(System.currentTimeMillis()/1000);
					u.setId(v);
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					u.setName(String.valueOf(v));
				}
				Thread.yield();
			}
		}
		
	}
	
	public static class ReadObjectThread extends Thread {
		
		@Override
		public void run() {
			while(true) {
				synchronized(u) {
					if (u.getId() != Integer.parseInt(u.getName())) { //如果 id 和 name 不一致的時候打印出來,說明 change 線程的執行不完整
						System.out.println(u.toString());
					}
				}
				Thread.yield();
			}
		}
		
	}
	
	public static void main(String[] args) throws InterruptedException {
		new ReadObjectThread().start();
		while(true) {
			Thread t = new ChangeObjectThread();
			t.start();
			Thread.sleep(150);
			t.stop(); //中斷線程
		}
	}
	
}

上面例子中,change 線程負責寫 user 實例,在完整的執行方法體的情況下,由於鎖的互斥,read 線程應該讀取到的 user id 和 name 一致。但是由於 stop 中斷 change 線程,導致 change 寫的過程只到一半。所以 read 線程會讀取到不一致的 id 和 name.

下面是輸出結果

User [id=1577372742, name=1577372741]
User [id=1577372742, name=1577372741]

解決這個問題很簡單,如下面這個例子,給 change 線程設置一個標記,由 change 線程自己決定什麼時候中斷自己。

package demo1;


public class StopSafeDemo {
	public static User u = new User();
	
	public static class User {
		
		private int id;
		
		private String name;

		public int getId() {
			return id;
		}

		public void setId(int id) {
			this.id = id;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}
		
		public User() {
			id = 0;
			name = "0";
		}

		@Override
		public String toString() {
			return "User [id=" + id + ", name=" + name + "]";
		}
		
	
	}
	
	public static class ChangeObjectThread extends Thread {
		
		volatile boolean stopme = false; //是否終端的標記
		
		public void stopMe() {
			stopme = true;
		}
		
		@Override
		public void run() {
			
			while(true) {
				if (stopme) { //在鎖外中斷線程,不影響 user 的完整性和一致性
					break;
				}
				synchronized(u) {
					int v = (int)(System.currentTimeMillis()/1000);
					u.setId(v);
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					u.setName(String.valueOf(v));
				}
				Thread.yield();
			}
		}
		
	}
	
	public static class ReadObjectThread extends Thread {
		
		@Override
		public void run() {
			while(true) {
				synchronized(u) {
					if (u.getId() != Integer.parseInt(u.getName())) {
						System.out.println(u.toString());
					}
				}
				Thread.yield();
			}
		}
		
	}
	
	public static void main(String[] args) throws InterruptedException {
		new ReadObjectThread().start();
		while(true) {
			ChangeObjectThread t = new ChangeObjectThread();
			t.start();
			Thread.sleep(150);
			t.stopMe(); //中斷 change 線程
		}
	}
}

上面的stopMe()方法是我們自定義的。JDK給我們提供了官方的api實現一樣的效果。

Thread.interrupt()   //中斷線程
Thread.isInterrupted()   //判斷線程是否中斷
Thread.interrupted()   //判斷線程是否中斷,並且清除中斷的標記,我的理解就是線程還沒有中斷的情況下用來反悔的

如下面的註釋所示,注意阻塞狀態下的異常,會清除中斷標記,需要重新設置中斷。

package demo1;

public class InterruptDemo {
	
	
	public static User u = new User();
	
	public static class User {
		
		private int id;
		
		private String name;

		public int getId() {
			return id;
		}

		public void setId(int id) {
			this.id = id;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}
		
		public User() {
			id = 0;
			name = "0";
		}

		@Override
		public String toString() {
			return "User [id=" + id + ", name=" + name + "]";
		}
		
	
	}
	
	public static class ChangeObjectThread extends Thread {
		
		@Override
		public void run() {
			while(true) {
				if (this.isInterrupted()) { //判斷自己是否中斷,停止線程
					break;
				}
				synchronized(u) {
					int v = (int)(System.currentTimeMillis()/1000);
					u.setId(v);
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
					    //注意此處。sleep爲阻塞狀態,這種狀態下中斷線程會出現上述異常,並且中斷標記位會被清除,所以需要重新調用interrupt()設置標記位
						// TODO Auto-generated catch block
						e.printStackTrace();
						this.interrupt(); 
					}
					u.setName(String.valueOf(v));
				}
				Thread.yield();
			}
		}
		
	}
	
	public static class ReadObjectThread extends Thread {
		
		@Override
		public void run() {
			while(true) {
				synchronized(u) {
					if (u.getId() != Integer.parseInt(u.getName())) {
						System.out.println(u.toString());
					}
				}
				Thread.yield();
			}
		}
		
	}
	
	public static void main(String[] args) throws InterruptedException {
		new ReadObjectThread().start();
		while(true) {
			Thread t = new ChangeObjectThread();
			t.start();
			Thread.sleep(150);
			t.interrupt(); //中斷線程
		}
	}

	
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章