如何正确停止一个线程

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. C#对 Dictionary进行排序 转

    C# .net 3.5 以上的版本引入 Linq 后,字典Dictionary排序变得十分简单,用一句类似 sql 数据库查询语句即可搞定:不过,.net 2.0 排序要稍微麻烦一点,为便于使用,将总 ...

  2. Git & Github 一页简明笔记

    由于小组工程需要使用git&github的版本控制来协作,但我对其使用并不熟悉,特此写篇一页的笔记放在手边,备随时查阅. 相信这种一页的简明笔记,对大家也是有帮助的.我的笔记总结自廖雪峰的Gi ...

  3. 开源搜索 Iveely Search Engine 0.6.0 发布 -- 黎明前的娇嫩

    快两年了,Iveely Search Engine已经走过了5个版本的岁月,虽出生“贫寒”,没有任何开源基金会的支持,没有优秀的“干爹.干妈”,它凭着它的爱好者的支持,0.6.0终于破壳而出,7年前, ...

  4. 【微收藏】来自Twitter的自动文字补齐jQuery插件 - Typeahead.js

    没图没逼格 事发有因 该插件可以结合本地数据进行一些操作.推荐关注一下H5的几种数据存储的方式(localstorage与sessionstorage.IndexedDB.离线缓存manifest文件 ...

  5. Use Windows Azure AD to create SSO projects

    Keywords Windows Azure AD, SSO Summary Use Windows Azure AD to create SSO projects Detailed Scenario ...

  6. 深入理解web项目的配置文件

    1.启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点. 2.紧急着,容创建一个Servl ...

  7. 给java应用打包

    C:\dollapp\classes> jar -cvf C:\dollapp\deploy\dollapp.jar *.* 上面的jar命令会把 C:\dollapp\classes 下的 所 ...

  8. usefull-url

    http://www.johnlewis.com/ http://codepen.io/francoislesenne/pen/aIuko http://www.rand.org/site_info/ ...

  9. 【CodeForces 567F】Mausoleum

    寒假最后一题补完啦 ^∀^ 题意 1到n每个数字有两个,排成先不降后不升的序列,比如112332,并且满足k个形如 3 <= 6 代表第三个数字要≤第六个数字这样的约束要求,求有多少种排法. 分 ...

  10. Mac OS X系统下编译运行C代码

    1.使用编译器将源文件中的代码转换为二进制代码,这个过程叫做编译. 将终端的工作路径切换到源文件所在的路径. cc -c 源文件的名称.例如:cc -c main.c 如果没有意外的话,就会在当前工作 ...