线程中断,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(); //中断线程
		}
	}

	
}

你可能感兴趣的:(java多线程)