Java并发编程笔记—基础知识—实用案例
如何正确停止一个线程
1)共享变量的使用
2)Thread.interrupt()的理解
正确的停止线程方式是设置共享变量,并调用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并发编程笔记—基础知识—实用案例的更多相关文章
- java并发编程笔记(六)——AQS
java并发编程笔记(六)--AQS 使用了Node实现FIFO(first in first out)队列,可以用于构建锁或者其他同步装置的基础框架 利用了一个int类型表示状态 使用方法是继承 子 ...
- java并发编程笔记(十一)——高并发处理思路和手段
java并发编程笔记(十一)--高并发处理思路和手段 扩容 垂直扩容(纵向扩展):提高系统部件能力 水平扩容(横向扩容):增加更多系统成员来实现 缓存 缓存特征 命中率:命中数/(命中数+没有命中数) ...
- java并发编程笔记(四)——安全发布对象
java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...
- java并发编程笔记(三)——线程安全性
java并发编程笔记(三)--线程安全性 线程安全性: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现 ...
- Java并发编程学习前期知识下篇
Java并发编程学习前期知识下篇 通过上一篇<Java并发编程学习前期知识上篇>我们知道了在Java并发中的可见性是什么?volatile的定义以及JMM的定义.我们先来看看几个大厂真实的 ...
- java并发编程 线程基础
java并发编程 线程基础 1. java中的多线程 java是天生多线程的,可以通过启动一个main方法,查看main方法启动的同时有多少线程同时启动 public class OnlyMain { ...
- java并发编程笔记(十)——HashMap与ConcurrentHashMap
java并发编程笔记(十)--HashMap与ConcurrentHashMap HashMap参数 有两个参数影响他的性能 初始容量(默认为16) 加载因子(默认是0.75) HashMap寻址方式 ...
- java并发编程笔记(九)——多线程并发最佳实践
java并发编程笔记(九)--多线程并发最佳实践 使用本地变量 使用不可变类 最小化锁的作用域范围 使用线程池Executor,而不是直接new Thread执行 宁可使用同步也不要使用线程的wait ...
- java并发编程笔记(八)——死锁
java并发编程笔记(八)--死锁 死锁发生的必要条件 互斥条件 进程对分配到的资源进行排他性的使用,即在一段时间内只能由一个进程使用,如果有其他进程在请求,只能等待. 请求和保持条件 进程已经保持了 ...
随机推荐
- 学习笔记——Maven 内置变量
Maven内置变量说明: ${basedir} 项目根目录(即pom.xml文件所在目录) ${project.build.directory} 构建目录,缺省为target目录 ${project. ...
- node 学习笔记 - fs 文件操作
本文同步自我的个人博客:http://www.52cik.com/2015/12/03/learn-node-fs.html 最近看到群里不少大神都开始玩 node 了,我感觉跟他们步伐越来越大了, ...
- [转]Extundelete--数据恢复软件
前言 作为一名运维人员,保证数据的安全是根本职责,所以在维护系统的时候,要慎之又慎,但是有时难免会出现数据被误删除的情况,在这个时候该如何快速.有效地恢复数据显得至关重要,extundelete就是其 ...
- js中的this中使用
请先查看:http://www.jb51.net/article/41656.htm 情况一:纯粹的函数调用 这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global. 情况二:作 ...
- Go语言开发环境搭建
1.Go的安装 (1)下载go安装程序 下载地址:https://golang.org/dl/ (墙内下载地址http://www.golangtc.com/download),如果是您的系统是win ...
- HTML上传文件写法
来源于:http://www.cnblogs.com/SkySoot/p/3525139.html html 表单上传文件 一般处理程序由于没有 apsx 页面的整个模型和控件的创建周期,而比较有效率 ...
- 【转】Eclipse下导入外部jar包的3种方式
我们在用Eclipse开发程序的时候,经常要用到第三方jar包.引入jar包不是一个小问题,由于jar包位置不清楚,而浪费时间.下面配图说明3种Eclipse引入jar包的方式. 1.最常用的普通 ...
- Java编程思想学习(一) 一切都是对象
前言 Java是基于C++的,但Java是一种更加纯粹的面向对象程序设计语言. C++和Java都是混合/杂合型语言.杂合型语言允许多种编程风格. 用引用操纵对象 每种编程语言都有自己操纵内存中元素的 ...
- 学习笔记-KMP算法
按照学习计划和TimeMachine学长的推荐,学习了一下KMP算法. 昨晚晚自习下课前粗略的看了看,发现根本理解不了高端的next数组啊有木有,不过好在在今天系统的学习了之后感觉是有很大提升的了,起 ...
- Matlab最短路径问题记录
利用graphshortestpath 可以求最短路径,具体用法参考MATLAB帮助 S=[1 1 2 2 3 3 4 4 4 4 5 6 6 7 8]; %起始节点向量 E=[2 3 5 4 4 6 ...