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并发编程笔记(八)--死锁 死锁发生的必要条件 互斥条件 进程对分配到的资源进行排他性的使用,即在一段时间内只能由一个进程使用,如果有其他进程在请求,只能等待. 请求和保持条件 进程已经保持了 ...
随机推荐
- 程序员"青春饭"问题之我见
程序员"青春饭"问题之我见 声明:转载请注明出处.http://www.cnblogs.com/hzg1981/ 1. 问题描述 问题1: 什么是程序员? 在本文中程序员的定义 ...
- js中的运动
缓慢隐藏与出现 效果: 鼠标移至分享上黄色区域自动向左隐藏. <!DOCTYPE html> <html> <head> <title></tit ...
- 编写高质量代码改善C#程序的157个建议[为泛型指定初始值、使用委托声明、使用Lambda替代方法和匿名方法]
前言 泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能.基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托 ...
- ubuntu的命令day1
ls -i 显示所有的文件,包括隐藏的文件. 以 . 开头的文件都是隐藏文件,可以在终端用ls -i显示所有的文件.比如.ssh linux生成密钥的命令如下: 1. cd .ssh/ ...
- Go语言开发环境搭建
1.Go的安装 (1)下载go安装程序 下载地址:https://golang.org/dl/ (墙内下载地址http://www.golangtc.com/download),如果是您的系统是win ...
- SSH框架总结(框架分析+环境搭建+实例源码下载)
来源于: http://blog.csdn.net/shan9liang/article/details/8803989 首先,SSH不是一个框架,而是多个框架(struts+spring+hiber ...
- POJ 2449Remmarguts' Date K短路模板 SPFA+A*
K短路模板,A*+SPFA求K短路.A*中h的求法为在反图中做SPFA,求出到T点的最短路,极为估价函数h(这里不再是估价,而是准确值),然后跑A*,从S点开始(此时为最短路),然后把与S点能达到的点 ...
- Spring 事物机制
Spring两种事物处理机制,一是声明式事物,二是编程式事物 声明式事物 1)Spring的声明式事务管理在底层是建立在AOP的基础之上的. 其本质是对方法前后进行拦截,然后在目标方法开始之前创建或 ...
- 【HDU 4150】Powerful Incantation
题 题意 给你s1,s2两个字符串,求s1中有多少个s2 代码 #include<stdio.h> #include<string.h> int t,len1,len2,pos ...
- Myeclipse快捷键的使用
存盘 Ctrl+s(肯定知道) 注释代码 Ctrl+/ 取消注释 Ctrl+\(Eclipse3已经都合并到Ctrl+/了) 代码辅助 Alt+/ 快速修复 Ctrl+1 代码格式化 Ctrl+Shi ...