1.要响应线程中断
线程接受到中断信号后要及时的对中断进行响应。响应方式:
1.捕捉InterruptException
for(;;){
try {
doXXX();
} catch (InterruptedException e) {
System.out.println(getName() +" is interrupt");
break;
}
catch (Exception e) {
e.printStackTrace();
}
}
2.判断当前线程状态
for(;;){
doXXX();
//判断是否被中断
if(Thread.interrupted()){
System.out.println(getName() +" is interrupt");
break;
}
}
接收到中断信号后,需要结束当前线程,可行的方式有return,break等,比较优雅的方式是抛出InterruptedException异常。代码如下:
public void foo() throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
2.使用ThreadLocal
顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
- 不使用ThreadLocal的测试结果
private int sum2 = 0;
public int getSum(){
sum2++;
return sum2;
}
public static void main(String[] args){
new CurrentTest().threadLocal();
}
public void threadLocal(){
TestThreadLocal t1 = new TestThreadLocal(this);
TestThreadLocal t2 = new TestThreadLocal(this);
TestThreadLocal t3 = new TestThreadLocal(this);
t1.setName("t1");
t2.setName("t2");
t3.setName("t3");
t1.start();
t2.start();
t3.start();
}
class TestThreadLocal extends Thread{
CurrentTest c;
public TestThreadLocal(CurrentTest c) {
this.c = c;
}
@Override
public void run() {
int sum = this.c.getSum();
while(sum<10){
System.out.println(getName()+" sum is "+sum);
sum = this.c.getSum();
}
}
}
输出结果:出现并发问题
- 使用ThreadLocal的测试结果
private ThreadLocal<Integer> sum = new ThreadLocal<Integer>(){
protected Integer initialValue() {return 0;};
};
public int getSum(){
sum.set(sum.get() + 1);
return sum.get();
}
public static void main(String[] args){
new CurrentTest().threadLocal();
}
public void threadLocal(){
TestThreadLocal t1 = new TestThreadLocal(this);
TestThreadLocal t2 = new TestThreadLocal(this);
TestThreadLocal t3 = new TestThreadLocal(this);
t1.setName("t1");
t2.setName("t2");
t3.setName("t3");
t1.start();
t2.start();
t3.start();
}
class TestThreadLocal extends Thread{
CurrentTest c;
public TestThreadLocal(CurrentTest c) {
this.c = c;
}
@Override
public void run() {
int sum = this.c.getSum();
while(sum<10){
System.out.println(getName()+" sum is "+sum);
sum = this.c.getSum();
}
}
}
输出结果:每个线程都从1一直计数到9,线程间没有出现并发问题
事实上ThreadLocal是牺牲空间来减少高并发所消耗的时间,其原理是每个Thread维护一个Map集合,集合的Key是ThreadLocal对象,value是共享变量的副本,这样每次Thread修改变量时就会直接修改本地保存的变量副本。具体ThreadLocal源代码如下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
注意:使用ThreadLocal,一般都是声明在静态变量中,如果不断的创建ThreadLocal而且没有调用其remove方法,将会导致内存泄露。
3.任务的提交者和执行者
为了方便并发执行任务,出现了一种专门用来执行任务的实现,也就是Executor。由此,任务提交者不需要再创建管理线程,使用更方便,也减少了开销。
java.util.concurrent.Executors是Executor的工厂类,通过Executors可以创建你所需要的Executor。
常用的子类如下图:(转自:http://blog.csdn.net/qq_29882587/article/details/78658675)
更广泛的使用是ExecutorSevice接口,主要API如下:
其中submit方法可以接收两类参数,Runable和Callable,Callable是需要有返回值的。submit方法返回Feture对象。Feture对象用于线程间的通信,Future通常包括get(阻塞至任务完成), cancel,get(timeout)(等待一段时间)等等。Future也用于异步变同步的场景。