当前位置: 首页 > news >正文

线程的正确停止

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

线程停止的几种方式:

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

转载于:https://my.oschina.net/u/2309504/blog/538873

相关文章:

  • C++类的存储
  • PHP+Swoole网络通信框架
  • spark-submit提交任务到集群
  • 结合Scikit-learn介绍几种常用的特征选择方法
  • js图片处理
  • bconsole中相关命名说明
  • Oracle更改redo log的大小
  • parallels desktop克隆ubuntu虚拟机网卡丢失
  • 泛型vector
  • [转]SQLServer 2008数据库查看死锁、堵塞的SQL语句
  • Solr5安装部署
  • Scrapy 入门:Hello Scrapy
  • 李嘉诚储藏财富的背后隐藏着什么奥秘?
  • Bitnami Redmine 与 gerrit 整合问题解决
  • 电商项目系列文档(三):秒杀的设计
  • 【译】JS基础算法脚本:字符串结尾
  • 【React系列】如何构建React应用程序
  • angular2 简述
  • co.js - 让异步代码同步化
  • download使用浅析
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • Git 使用集
  • HTTP请求重发
  • js中的正则表达式入门
  • LeetCode29.两数相除 JavaScript
  • Linux gpio口使用方法
  • mysql外键的使用
  • RxJS: 简单入门
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 前端面试之闭包
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 你对linux中grep命令知道多少?
  • #Java第九次作业--输入输出流和文件操作
  • (LeetCode 49)Anagrams
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (九)c52学习之旅-定时器
  • (四)图像的%2线性拉伸
  • (转载)CentOS查看系统信息|CentOS查看命令
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .equals()到底是什么意思?
  • .gitignore文件设置了忽略但不生效
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET 8.0 中有哪些新的变化?
  • .NET 发展历程
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .net反编译工具
  • .NET学习全景图
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • @ComponentScan比较
  • @RequestBody与@ModelAttribute
  • @Resource和@Autowired的区别