正常来说线程里的代码执行完之后线程就自动中断了,但是一些处于无线循环当中的线程需要另外通过程序进行中断。
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(); //中断线程
}
}
}
本文探讨了Java中线程中断的多种方式,包括废弃的stop()方法、自定义标记中断和官方推荐的interrupt()方法。通过具体示例,详细阐述了如何避免线程中断时的数据不一致问题。
622

被折叠的 条评论
为什么被折叠?



