如何正确停止一个线程

1)共享变量的使用

中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务。当线程等待某些事件发生而被阻塞,又会发生什么?果线程被阻塞,它便不能核查共享变量,也就不能停止。这在许多情况下会发生,例如调用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()时。
 

2)Thread.interrupt()的理解

Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
 

正确的停止线程方式是设置共享变量,并调用interrupt()(注意变量应该先设置)。如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。在任何一种情况中,最后线程都将检查共享变量然后再停止。

实例如下:

package com.iflytek.ossp.bliserver.common.utils;

public abstract class MyWorkRunnable implements Runnable {
volatile Thread mTheThread = null; @Override
public void run() {
if (mTheThread != Thread.currentThread()) {
throw new RuntimeException();
} while (!Thread.interrupted() && mTheThread != null) {
execute();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
mTheThread.interrupt();
}
}
} public void start() {
mTheThread = new Thread(this);
mTheThread.start();
} public void stop() {
if (mTheThread != null) {
mTheThread.interrupt();
try {
mTheThread.join(); // 等待线程彻底结束
} catch (InterruptedException e) {
e.printStackTrace();
mTheThread.interrupt();
}
}
} public abstract void execute();
}

在现有的线程安全类中添加功能

假设有一个线性的链表,需要提供一个原子的“若没有则添加”的操作。

第一种方法:扩展现有的类

public class BetterVector<E> extends Vector<E> {

    public synchronized boolean putIfAbsent(E x) {
boolean absent = !contains(x);
if (absent)
add(x);
return absent; }
}

第二种方法:客户端加锁

注意下面这张方法是不能实现线程安全的。

public class ListHelper<E> {

    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
} }

问题出在使用错误的锁进行同步,无论List使用哪一个锁来保护它的状态,可以确定的是这个锁并不是ListHelper上的锁。

正确的方式是是使用客户端加锁:

public class ListHelper<E> {

    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public boolean putIfAbsent(E x) {
synchronized (list) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
}

但这张方法是脆弱的,因为需要依赖List的加锁策略,只有在遵循加锁策略上的类使用客户端加锁才有效。

最好的方式是使用组合:

public class ImprovedList<T> implements List<T> {

    private final List<T> list;

    public ImprovedList(List<T> list) {
this.list = list;
} public synchronized boolean putIfAbsent(T x) {
boolean contains = !list.contains(x);
if (contains)
list.add(x);
return contains;
} public synchronized void clear() {
list.clear();
} // ......按照类似的方式委托list的其他方法。
}

ThreadLocal的使用

下面是一个使用ThreadLocal来记录线程中各个方位调用耗时的帮助类。

import java.util.Date;
import java.util.HashMap;
import java.util.Map; /**
* 用于跟踪各个线程中方法调用的耗时等信息。
* <p>
* 对于一个线程串行多次执行同一个方法,只能跟踪到最后一次的执行信息。
*
* @author jdzhan,2013-1-13
*
*/
public final class MethodCallTrace { private static final ThreadLocal<Map<String, TraceInfo>> mutilThreadTraces = new ThreadLocal<Map<String, TraceInfo>>() {
@Override
protected Map<String, TraceInfo> initialValue() {
return new HashMap<String, TraceInfo>();
}
}; /**
* 开始跟踪
*/
public static void start() {
String funcName = new Throwable().getStackTrace()[1].getMethodName();
start(funcName);
} /**
* 结束跟踪
*/
public static void end() {
String funcName = new Throwable().getStackTrace()[1].getMethodName();
end(funcName);
} /**
* 开始跟踪
*
* @param funcName
* 指定一个方法名称
*/
public static void start(String funcName) { TraceInfo item = new TraceInfo();
item.setStartTime(new Date()); Map<String, TraceInfo> traces = null;
if (mutilThreadTraces.get() == null) {
traces = new HashMap<String, TraceInfo>();
mutilThreadTraces.set(traces);
} else {
traces = mutilThreadTraces.get();
} traces.put(funcName, item);
} /**
* 结束跟踪
*
* @param funcName
* 指定一个方法名
*/
public static void end(String funcName) {
Map<String, TraceInfo> traces = mutilThreadTraces.get();
if (traces == null) {
return;
}
TraceInfo item = traces.get(funcName);
if (item == null) {
return;
}
item.setEndTime(new Date());
} public static Map<String, TraceInfo> getTrace() {
return mutilThreadTraces.get();
}
}

MethodCallTrace

import java.util.Date;

public class TraceInfo {

    private Date startTime;
private Date endTime; public Date getStartTime() {
return new Date(startTime.getTime());
} public void setStartTime(Date startTime) {
this.startTime = new Date(startTime.getTime());
} public Date getEndTime() {
return new Date(endTime.getTime());
} public void setEndTime(Date endTime) {
this.endTime = new Date(endTime.getTime());
} /**
* 获取方法调用的耗时,如果返回-1或小于0说明记录存在问题。
*
* @return
*/
public long getCallTime() {
if (startTime == null || endTime == null) {
return -1;
}
return endTime.getTime() - startTime.getTime();
}
}

TraceInfo

import java.util.Map;
import java.util.Map.Entry; public class ThreadLocalTest { /**
* @param args
*/
public static void main(String[] args) {
MethodCallTrace.start();
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() { @Override
public void run() {
test();
test2();
test3();
Map<String, TraceInfo> trace = MethodCallTrace.getTrace(); for (Entry<String, TraceInfo> entry : trace.entrySet()) {
System.out.println(Thread.currentThread().getId() + entry.getKey() + ":" + entry.getValue().getCallTime() + "毫秒");
}
}
}).start();
} while (true) {
try {
Thread.sleep(3000);
MethodCallTrace.end();
Map<String, TraceInfo> trace = MethodCallTrace.getTrace();
for (Entry<String, TraceInfo> entry : trace.entrySet()) {
System.out.println("主线程:" + entry.getKey() + ":" + entry.getValue().getCallTime() + "毫秒");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} private static void test() {
MethodCallTrace.start();
for (int i = 0; i < 1; i++) {
try {
Thread.sleep(Thread.currentThread().getId());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
MethodCallTrace.end();
} private static void test2() {
MethodCallTrace.start("test2");
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(Thread.currentThread().getId());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
MethodCallTrace.end("test2");
} private static void test3() {
MethodCallTrace.start("test3");
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(Thread.currentThread().getId());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
MethodCallTrace.end("test3");
}
}
待补充……
 
 

Java并发编程笔记—基础知识—实用案例的更多相关文章

  1. java并发编程笔记(六)——AQS

    java并发编程笔记(六)--AQS 使用了Node实现FIFO(first in first out)队列,可以用于构建锁或者其他同步装置的基础框架 利用了一个int类型表示状态 使用方法是继承 子 ...

  2. java并发编程笔记(十一)——高并发处理思路和手段

    java并发编程笔记(十一)--高并发处理思路和手段 扩容 垂直扩容(纵向扩展):提高系统部件能力 水平扩容(横向扩容):增加更多系统成员来实现 缓存 缓存特征 命中率:命中数/(命中数+没有命中数) ...

  3. java并发编程笔记(四)——安全发布对象

    java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...

  4. java并发编程笔记(三)——线程安全性

    java并发编程笔记(三)--线程安全性 线程安全性: ​ 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现 ...

  5. Java并发编程学习前期知识下篇

    Java并发编程学习前期知识下篇 通过上一篇<Java并发编程学习前期知识上篇>我们知道了在Java并发中的可见性是什么?volatile的定义以及JMM的定义.我们先来看看几个大厂真实的 ...

  6. java并发编程 线程基础

    java并发编程 线程基础 1. java中的多线程 java是天生多线程的,可以通过启动一个main方法,查看main方法启动的同时有多少线程同时启动 public class OnlyMain { ...

  7. java并发编程笔记(十)——HashMap与ConcurrentHashMap

    java并发编程笔记(十)--HashMap与ConcurrentHashMap HashMap参数 有两个参数影响他的性能 初始容量(默认为16) 加载因子(默认是0.75) HashMap寻址方式 ...

  8. java并发编程笔记(九)——多线程并发最佳实践

    java并发编程笔记(九)--多线程并发最佳实践 使用本地变量 使用不可变类 最小化锁的作用域范围 使用线程池Executor,而不是直接new Thread执行 宁可使用同步也不要使用线程的wait ...

  9. java并发编程笔记(八)——死锁

    java并发编程笔记(八)--死锁 死锁发生的必要条件 互斥条件 进程对分配到的资源进行排他性的使用,即在一段时间内只能由一个进程使用,如果有其他进程在请求,只能等待. 请求和保持条件 进程已经保持了 ...

随机推荐

  1. 学习笔记——Maven超级POM

    2014-07-04:更新如何在安装程序中找到超级pom文件.Maven有一个超级POM,所有的POM均继承此文件.你可以使用解压工具打开jar文件$M2_HOME/lib/maven-model-b ...

  2. Scrum敏捷精要

    本文抽取Scrum中的一些重要思想和概念,对Scrum敏捷执行的主题流程进行精要的介绍. 一.基本思想 个体和互动   高于   流程和工具 工作的软件   高于   详尽的文档 客户合作      ...

  3. Android requires compiler compliance level 5.0 or 6.0. Found '1.7' instead. Please use Android Tool

    重装操作系统后,要重新配置Android开发环境.配置成功后,添加原本项目时却出现了错误! Android requires compiler compliance level 5.0 or 6.0. ...

  4. JavaScript并非“按值传递”

    置顶文章:<纯CSS打造银色MacBook Air(完整版)> 上一篇:<拥Bootstrap入怀--模态框(modal)篇> 作者主页:myvin 博主QQ:85139910 ...

  5. 分页pagination实现及其应用

    1.分页jquery.page.js //分页插件 /** 2014-08-05 ch **/ (function ($) { var ms = { init: function (obj, args ...

  6. js与jquery的区别

    var html = $('<a target="_blank" href="' + adCompContent.clickURL + '">< ...

  7. json和cookie兼容以前的

    'json': function(data) { try { if (typeof data === "string") { if (typeof JSON != 'undefin ...

  8. 使用background和background-image对CSS优先级造成影响

    在写一个关于背景图的CSS时候发现一个奇怪的现象, 原图: 如下代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitiona ...

  9. 幻灯片插件FlexSlider -- Amaze UI幻灯片参数

    用了Amaze UI 的人就知道,其幻灯片插件是 data-am-slider='{}'来传参数的:(以下代码来自Amaze UI 官网) <div data-am-widget="s ...

  10. Throwable和Exception的区别

    Java语言要求java程序中(无论是谁写的代码)所有抛出(throw)的异常都必须是从Throwable派生而来.当然,实际的Java编程中,由于JDK平台已经为我们设计好了非常丰富和完整的异常对象 ...