线程的正确停止

二次信任 提交于 2020-03-02 17:04:17

线程停止的几种方式:

  1. 线程运行完成,正常停止

  2. 执行stop方法,暴力停止,现已弃用

  3. 执行interrupt方法,现在使用。

stop方法为什么被弃用?

使用stop方法终止线程会释放掉此线程锁定的所有的监视器,如果线程修改了锁定对象的内容在还没有被正常处理之前线程被终止了。将会造成数据不一致的后果。

例如银行取款的例子:在线程A中进行取款操作,取款操作是一个同步方法,其中共有三个步骤:输入密码,取钱,修改余额。当用户a在输入密码,取钱之后,线程A.stop();线程终止。被修改的余额还没有写回数据库,从而造成数据混乱。

举例说明:首先定义一个操作类Login

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方法

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.

程序运行结果:

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时才会抛出异常

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!