重要的包:java.util.concurrent
多线程中关于线程任务的定义需要明确如下三个东西:Thread(类),Runnable(interface),Callable(interface)
本质上,每个当前运行的Thread都会持有一个Map,ThreadLocal类对这个Map的访问进行了封装,因此在线程中可以把一个新生成的对象通过ThreadLocal放入这个Map,这样可以保证该线程在以后每次从ThreadLocal对象即这个Map中取得的对象都只是在该线程中可用,不会被其它线程访问到。
To cache objects which you need frequently
ThreadLocal<T>
set(T value) //设置这个ThreadLocal的变量的值(对于当前线程的)。
2.1 Runnable
LinkedBlockingQueue
在BlockingQueue中,要使用put和take,而非offer和poll。如果要使用offer和poll,也是要使用带等待时间参数的offer和poll。
使用drainTo批量获得其中的内容,能够减少锁的次数
BlockingQueue<E>:
put(E)
take() : E
offer(E, long, TimeUnit) : boolean
poll(long, TimeUnit) : E
remainingCapacity()
drainTo(Collection<? super E>) : int
drainTo(Collection<? super E>, int ) : int
重入锁(ReentrantLock)是一种递归无阻塞的同步机制
AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference
法使得能够支持多个Writer并发。 ConcurrentHashMap需要使用更
多的内存。
使用支持CAS的数据结构,避免使用锁,如:
while(true){
if(A==V){
V=B;
return;
}
else{
A==V;
}
}
关键方法:
final CountDownLatch compeletTask = new CountDownLatch(COUNT):初始化,和并发数相等
CountDownLatch.countDowm():该方法实现计数功能,完成一个线程任务,数据减1
CountDownLatch.await():等待数据归0
例子:
或者當你啓動很多線程时,希望所有线程等到通知后才去执行:
8.2.CyclicBarrier
现 BSD 内核中定时器的,后来被广泛 移植到诸如 ACE 等框架中,堪称 BSD 中经典算法之
一,能针 对定时器的各类常见操作提供接近常数时间的响应,且能根据需 要很容易进行
扩展
多线程中关于线程任务的定义需要明确如下三个东西:Thread(类),Runnable(interface),Callable(interface)
1.ThreadLocal
顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量本质上,每个当前运行的Thread都会持有一个Map,ThreadLocal类对这个Map的访问进行了封装,因此在线程中可以把一个新生成的对象通过ThreadLocal放入这个Map,这样可以保证该线程在以后每次从ThreadLocal对象即这个Map中取得的对象都只是在该线程中可用,不会被其它线程访问到。
使用场景:
To keep state with a thread (user-id, transaction-id, logging-id)To cache objects which you need frequently
隐式传参
ThreadLocal<T>
initialValue() : T // 返回ThreadLocal变量的初始值。在一个线程中,如果在调用set()之前调用get()时候,initialValue() 就会被调用,而且也只会被调用一次。如果在调 用get()之前已经调用过set()了,那么initialValue()就不会被调用。
get() : T //得到这个ThreadLocal变量的值(对于当前线程的)。set(T value) //设置这个ThreadLocal的变量的值(对于当前线程的)。
remove()
例子:
public class MyThread extends Thread {
@SuppressWarnings("rawtypes")
public static ThreadLocal tl = new ThreadLocal();
@SuppressWarnings("unchecked")
public void run() {
tl.set(new Client(20));
Client c= (Client) tl.get();
int r = new Random().nextInt(100);
System.out.println(r);
c.clNum=c.clNum+r;
tl.set(c);
System.out.println("Thread " + Thread.currentThread().getName() +
" has client " +tl.get());
}
private class Client {
private int clNum;
Client(int n) {
clNum = n;
}
public String toString() {
return "Client[" + clNum + "]";
}
}
}
public class ThreadLocalTest{
/**
* @param args
*/
public static void main(String[] args) {
Thread t = new MyThread();
Thread t1 = new MyThread();
t.start();
t1.start();
}
}
2.任务:
2.1 Runnable
众所周知,省略
2.2 Callable:需要返回值的任务
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Object> task = new Callable<Object>() {
public Object call() throws Exception {
Object result = "...";
return result;
}
};
Future<Object> future = executor.submit(task);
future.get();
Future<Object> future = executor.submit(task);
// 等待到任务被执行完毕返回结果
// 如果任务执行出错,这里会抛ExecutionException
future.get();
//等待3秒,超时后会抛TimeoutException
future.get(3, TimeUnit.SECONDS);
3.阻塞队列:
ArrayBlockingQueueLinkedBlockingQueue
SynchronousQueue
在BlockingQueue中,要使用put和take,而非offer和poll。如果要使用offer和poll,也是要使用带等待时间参数的offer和poll。
使用drainTo批量获得其中的内容,能够减少锁的次数
BlockingQueue<E>:
put(E)
take() : E
offer(E, long, TimeUnit) : boolean
poll(long, TimeUnit) : E
remainingCapacity()
drainTo(Collection<? super E>) : int
drainTo(Collection<? super E>, int ) : int
正确用法:
final BlockingQueue<Object> blockingQ = new ArrayBlockingQueue<Object>(10);
Thread thread = new Thread("consumer thread") {
public void run() {
for (;;) {
try {
Object object = blockingQ.take(); // 等到有数据才继续
handle(object);
} catch (InterruptedException e) {
break;
} catch (Exception e) {
// handle exception
}
}
}
};
4.锁
4.1ReentrantLock和Synchronized
Synchronized是Lock的一种简化实现,一个Lock可以对应多个Condition,而synchronized把Lock和Condition合并了,一个synchronized Lock只对应一个Condition,可以说Synchronized是Lock的简化版本。在JDK 5,Synchronized要比Lock慢很多,但是在JDK 6中,它们的效率差不多。重入锁(ReentrantLock)是一种递归无阻塞的同步机制
4.2 使用AtomicInteger
class Counter {
private volatile int count = 0;
//若要线程安全执行执行count++,需要加锁
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
class Counter {
private AtomicInteger count = new AtomicInteger();
//使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
java.util.concurrent中实现的原子操作类包括:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference
5.使用Lock-Free算法
class Counter {
private volatile int max = 0;
//若要线程安全,需要加锁
public synchronized void set(int value) {
if (value > max) {
max = value;
}
}
public int getMax() {
return max;
}
}
class Counter {
private AtomicInteger max = new AtomicInteger();
public void set(int value) {
//LockFree算法,不需要加锁。
//通常都是三个部分组成:
//1 循环
//2 CAS (CompareAndSet)
//3 回退
//循环
for (;;) {
int current = max.get();
if (value > current) {
//CAS
if (max.compareAndSet(current, value)) {
break;
} else {
//回退
continue;
}
} else { break; }
}
}
public int getMax() {
return max.get();
}
}
<span style="font-size:18px;">对比如下两种线程安全实现
class BeanManager {
private Map<String, Object> map = new HashMap<String, Object>();
public Object getBean(String key) {
synchronized (map) {
Object bean = map.get(key);
if (bean == null) {
map.put(key, createBean());
bean = map.get(key);
}
return bean;
}
}
}
class BeanManager {
private ConcurrentMap<String, Object> map = new ConcurrentHashMap<String, Object>();
public Object getBean(String key) {
Object bean = map.get(key);
if (bean == null) {
//使用ConcurrentMap,避免直接使用锁,锁由数据结构来管理。
map.putIfAbsent(key, createBean());
bean = map.get(key);
}
return bean;
}
}</span>
ConcurrentHashMap并没有实现Lock-Free,只是使用了分离锁的办法使得能够支持多个Writer并发。 ConcurrentHashMap需要使用更
多的内存。
6.使用CopyOnWriteArrayList
对比如下两种实现:
class Engine {
private List<Listener> listeners = new ArrayList<Listener>();
public boolean addListener(Listener listener) {
synchronized (listeners) {
return listeners.add(listener);
}
}
public void doXXX() {
synchronized (listeners) {
for (Listener listener : listeners) {
listener.handle();
}
}
}
}
适当使用CopyOnWriteArrayList,能够提高
读操作时的效率。
class Engine {
private List<Listener> listeners = new CopyOnWriteArrayList <Listener>();
public boolean addListener(Listener listener) {
return listeners.add(listener);
}
public void doXXX() {
for (Listener listener : listeners) {
listener.handle();
}
}
}
使用支持CAS的数据结构,避免使用锁,如:
dAtomicXXX、ConcurrentMap、CopyOnWriteList、ConcurrentLinkedQueue
7.CAS:Compare and Swap, 比较并交换。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。while(true){
if(A==V){
V=B;
return;
}
else{
A==V;
}
}
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
8.线程控制:
8.1.CountDownLatch
等待多个线程都结束,再执行后续代码关键方法:
final CountDownLatch compeletTask = new CountDownLatch(COUNT):初始化,和并发数相等
CountDownLatch.countDowm():该方法实现计数功能,完成一个线程任务,数据减1
CountDownLatch.await():等待数据归0
例子:
final int COUNT=10;
final CountDownLatch compeletTask = new CountDownLatch(COUNT);
for (int i = 0; i < COUNT; i++) {
Thread thread = new Thread("workor"+i){
public void run(){
long time = 5*1000;
try {
Thread.sleep(time);
System.out.println(Thread.currentThread().getName()+"---execute-----");
compeletTask.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
}
compeletTask.await();
System.out.println("-----finish------");
或者當你啓動很多線程时,希望所有线程等到通知后才去执行:
final CountDownLatch compeletTask = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
Thread thread = new Thread("workor"+i){
public void run(){
try {
compeletTask.await();//等待通知
//do something
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName()+"---execute-----");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
}
compeletTask.countDown(); //通知
8.2.CyclicBarrier
这个类可以这么理解,这是一个对多线程进行协调的类,当启动多个线程后,等待多个线程都完成各自的任务后,最后到达barrier的线程,然后去执行barrier中定义的任务
例子public class PerformaceTest {
private int threadCount;
private CyclicBarrier barrier;
private int loopCount = 10;
public PerformaceTest(int threadCount){
this.threadCount=threadCount;
//参数threadCount 定义的是多少个线程任务到达barrier后,去执行Runnable中的任务,
//如果此处的threadCount比并发的线程数大,那么就不会执行这个任务
//通过日志发现,该任务的执行是最后一个达到barrier的线程执行这个Runnable
barrier = new CyclicBarrier(threadCount, new Runnable() {
@Override
public void run() {
collectTestResult();
}
});
for (int i = 0; i <threadCount; i++) {
Thread thread = new Thread("test-thread "+i){
public void run(){
for (int j = 0; j <loopCount; j++) {
doTest();
}
try {
barrier.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) { // TODO Auto-generated catch block
e.printStackTrace();
}
}
};
thread.start();
}
}
private void collectTestResult() {
System.out.println(Thread.currentThread().getName()+" collectTestResult");
}
private void doTest(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) { // TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" doTest");
}
public static void main(String[] args) {
new PerformaceTest(5);
}
}
9.定时任务
备注:有了ScheduledExecutorService,不建议你再使用java.util.Timer,因为它无论功能性能都不如ScheduledExecutorService。ScheduledExecutorService接口:schedule(Runnable command, long delay, TimeUnit unit) : ScheduledFutureschedule(Callable<V> callable, long delay, TimeUnit unit) : ScheduledFuturescheduleAtFixedRate(Runnable comand, long initDelay, long period, TimeUnit unit) : ScheduledFuturescheduleWithFixedDelay(Runnable command, long initDelay, long delay, TimeUnit unit) : ScheduledFuture例子
public class ScheduledExecutorServiceTest {
public static void main(String[] args) {
ScheduledExecutorService ss = Executors.newScheduledThreadPool(2);
Thread thread1 = new Thread("thead1") {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" *****");
}
};
Thread thread2 = new Thread("thead2") {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" --------");
}
};
Thread thread3 = new Thread("thead3") {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" &&&&&");
}
};
ScheduledFuture<?> future = ss.scheduleAtFixedRate(thread1, 2, 2, TimeUnit.SECONDS);
ScheduledFuture<?> future1 = ss.scheduleAtFixedRate(thread2, 2, 2, TimeUnit.SECONDS);
ScheduledFuture<?> future2 = ss.scheduleAtFixedRate(thread3, 2, 2, TimeUnit.SECONDS);
}
}
9.大规模定时器TimerWheel
存在一种算法 TimerWheel ,适用于大规模的定时器实现。这个 算法最早是被设计用来实现 BSD 内核中定时器的,后来被广泛 移植到诸如 ACE 等框架中,堪称 BSD 中经典算法之
一,能针 对定时器的各类常见操作提供接近常数时间的响应,且能根据需 要很容易进行
扩展