1. 无论何种方式,启动一个线程,就要给它一个名字!这对排错诊断系统监控有帮助。否则诊断问题时,无法直观知道某个线程的用途。
2. 程序应该对线程中断作出恰当的响应。
3. ThreadLocal
local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
注意:使用ThreadLocal,一般都是声明在静态变量中,如果不断的创建ThreadLocal而且没有调用其remove方法,将会导致内存泄露。
4、任务的提交者和执行者
为了方便并发执行任务,出现了一种专门用来执行任务的实现,也就是Executor。由此,任务提交者不需要再创建管理线程,使用更方便,也减少了开销。
java.util.concurrent.Executors是Executor的工厂类,通过Executors可以创建你所需要的Executor。有两种任务:Runnable、Callable。Callable是需要返回值的任务。
Task Submitter把任务提交给Executor执行,它们之间需要一种通讯手段,这种手段的具体实现,通常叫做Future。Future通常包括get(阻塞至任务完成),cancel,get(timeout)(等待一段时间)等等。Future也用于异步变同步的场景。
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callable<Object> task = new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(1000);
return "222";
}
};
Future<Object> future = executorService.submit(task);
try {
//future.get(3, TimeUnit.SECONDS); //等待三秒后超时,表示执行失败
if(!future.cancel(true)){
System.out.println(future.get());
}
} catch (InterruptedException | ExecutionException e) {
System.out.println("execute failed");
e.printStackTrace();
}
System.out.println("sth");
5、阻塞队列
阻塞队列,是一种常用的并发数据结构,常用于生产者-消费者模式。
在Java中,有三种常用的阻塞队列:
ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
blockingQ.put(object);// 如果队列满则阻塞
blockingQ.take(); // 如果队列空则阻塞
blockingQ.poll(2, TimeUnit.SECONDS); //防止死等
6、要执行wait操作,必须先取得该对象的锁。执行wait操作之后,锁会释放。被唤醒之前,需要先获得锁。
synchronized (xxxx) {
xxxx.wait();
}
要执行notify和notifyAll操作,都必须先取得该对象的锁。
synchronized (xxxx) {
xxxx.notify();
}
未取得锁就直接执行wait、notfiy、notifyAll会抛异常IllegalMonitorStateException。
7、ReentrantLock和Synchronized
Synchronized是Lock的一种简化实现,一个Lock可以对应多个Condition,而synchronized把Lock和Condition合并了,一个synchronizedLock只对应一个Condition,可以说Synchronized是Lock的简化版本。
不要在Lock和Condition上使用wait、notiffy、notifyAll方法,而是使用如下对应的方法。
awati-> wait
singal-> notify
singalAll-> notifyAll
8、Java.util.concurrent中实现的原子操作类包括:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference。
9、ForkJoinPool
ForkJoinPool 是 Java SE 7 新功能“分叉/结合框架”的核心类。分叉/结合框架是一个比较特殊的线程池框架,专用于需要将一个任务不断分解成子任务(分叉),再不断进行汇总得到最终结果(结合)的计算过程。
子任务由 ForkJoinTask 的实例来代表。它是一个抽象类,JDK 为我们提供了两个实现:RecursiveTask 和 RecursiveAction,分别用于需要和不需要返回计算结果的子任务。ForkJoinTask 提供了三个静态的 invokeAll 方法来调度子任务,注意只能在 ForkJoinPool 执行计算的过程中调用它们。
以下代码计算磁盘指定目录里的子文件数
package xjt.thread;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class CountFileTaskRecursive extends RecursiveTask<Integer> {
private static final long serialVersionUID = 1L;
int sum = 0;
File file;
public CountFileTaskRecursive(File file) {
this.file = file;
}
@Override
protected Integer compute() {
Integer csum = 0;
List<CountFileTaskRecursive> tasklist = new ArrayList<CountFileTaskRecursive();
if (file.isDirectory()){
for (File f : file.listFiles()){
CountFileTaskRecursive t = new CountFileTaskRecursive(f);
tasklist.add(t);
}
} else{
csum++;
}
if (!tasklist.isEmpty()){
for (CountFileTaskRecursive t : invokeAll(tasklist)){
csum += (Integer) t.join();
}
}
return csum;
}
public static void main(String[] args) {
CountFileTaskRecursive task = new CountFileTaskRecursive(new File("F:/soft"));
Integer sum = (Integer) new ForkJoinPool().invoke(task);
System.out.println(sum);
}
}
10、CountDownLatch
Java的concurrent包里面的CountDownLatch其实可以把它看作一个计数器,只不过这个计数器的操作是原子操作,同时只能有 一个线程去操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。你可以向CountDownLatch对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数 器的计数值被其他的线程减为0为止。
参见http://zapldy.iteye.com/blog/746458
11、ConcurrentHashMap
参见http://www.infoq.com/cn/articles/ConcurrentHashMap
12、Thread.join()
public final void join() throws InterruptedException Waits for this thread to die. Throws: InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
参见http://my.oschina.net/zyc1016/blog/138581