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并发编程笔记(八)--死锁 死锁发生的必要条件 互斥条件 进程对分配到的资源进行排他性的使用,即在一段时间内只能由一个进程使用,如果有其他进程在请求,只能等待. 请求和保持条件 进程已经保持了 ...
随机推荐
- 20145208《Java程序设计》第3周学习总结
20145208 <Java程序设计>第3周学习总结 教材学习内容总结 认识对象 类类型 在第三章的学习中,我了解到JAVA可区分为基本类型和类类型两种类型,在上周的学习中我学习了JAVA ...
- RxJava简介
RxJava简介 本文为前段时间学习RxJava时留下的历史遗留笔记,仅作纪念,科学的大神教学帖子在这里-> 给 Android 开发者的 RxJava 详解 通过链式调用序列实现基于事件流的异 ...
- Android开发新手第一要素
很多新手开发程序的时候,或者将原来跑在Android 2.X上的程序迁移到Android 3.x以上的时候经常会莫名其妙的出现崩溃(Crash).从我的经验来看,这里可能有很多原因,但是最重要也是最常 ...
- 父div高度和宽度的应用
这是我自己在仿腾讯首页时遇到的布局问题,在此记录,如果有错,欢迎指正. 首先是对齐问题,可以把父div的高度设置为0,然后调整padding值,这样可以批量调整子div们和其他父div的相对高度. 这 ...
- Flask 开发全球化应用
从C#世界迈入python总是令人有一种如释重负之感,同样的效果同样的功能,只需要付出1/10不到的代价,可能正是Python所倡导的简美哲学所带来的威力. 我还深深地记得在ASP.NET中做全球化的 ...
- IT技术的进化道路
IT技术是人类发明的,同时也是给人类服务的,这项技术的进化,就是距离人越来越近,大体上是: 大型机时代: 离人类的距离超过100米. 小型机时代: 离人类的距离前进到10米. PC机时代: 离人类的距 ...
- 编写高质量代码改善C#程序的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]
前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正. 建议1. ...
- AngularJS开发指南2:AngularJS初始化过程
自动初始化 请将ng-app指令放到你应用的标签节点中, 如果你想要AngularJS自动执行整个<html>程序就把它放在 <html> 标签中.比如:<html ng ...
- angular的编辑器tinymce
angular的插件的确挺少的, 编辑器更是少, ui-tinymce是angular-ui推荐的一款编辑器(GIT: https://github.com/angular-ui/ui-tinymce ...
- 微信WeixinJSBridge API
<!DOCTYPE html><html> <head> <title>微信WeixinJSBridge API</title> <m ...