正常來說線程裏的代碼執行完之後線程就自動中斷了,但是一些處於無線循環當中的線程需要另外通過程序進行中斷。
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(); //中斷線程
}
}
}