据说在java 1.5之前,如果涉及到多线程的场景,大家只能使用从1.0开始就存在的一个接口: Runnable,
或者说使用实现了Runnabel接口的Thread类
在1.5版本时,Doug Lea 在版本中编写了java.util.concurrent
,让线程的使用变得更加简单
笔者使用的jdk版本为1.9,但是考虑到1.9使用的人不多,在这里还是讨论1.8版本的内容。
在这个版本的java.util.concurrent
包内,大致分了一下类:
atomic
atomic包内包含有一系列支持原子操作的类,最常用的一般为AtomicBoolean
、AtomicInteger
和AtomicLong
在这些类中,涉及到值的操作,都是调用的Unsafe中对应方法(或者是VarHandle中对应方法,参见Unsafe -> VarHandle
以AtomicInteger为例,这里的addAndGet
方法实际调用的是VarHandle#getAndAdd
/**
* Atomically adds the given value to the current value,
* with memory effects as specified by {@link VarHandle#getAndAdd}.
*
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int delta) {
return U.getAndAddInt(this, VALUE, delta) + delta;
}
locks
locks包内包含锁相关的类和接口,比如最核心的lock接口,其主要方法有lock()
、tryLock()
、unlock()
,这里不做过多介绍
Lock
接口
locks包内包含 Lock
、Condition
相关内容
其中Lock
接口
* {@code Lock} implementations provide more extensive locking
* operations than can be obtained using {@code synchronized} methods
* and statements. They allow more flexible structuring, may have
* quite different properties, and may support multiple associated
* {@link Condition} objects.
简单来说,Lock
接口实际上是对 synchronized的一个扩展,并提供了更加灵活的操作,比如其tryLock()
方法,并且支持Condition
对象,关于Condition
,我们稍后会进行讨论
Lock
接口的基本方法有:
void lock();
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
ReadWriteLock
接口
在实际的场景中,大部分的情况下我们是读取数据,并不是修改或者写入数据,而传统的synchronized
关键字并不支持对读写场景的区分,在jdk1.5之后也加入了ReadWriteLock
接口,提供读写锁的支持
每个读写锁由读锁和写锁构成:
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing
*/
Lock writeLock();
}
必须注意的是,所有实现ReadWriteLock
实现必须保证,成功获取readLock
的线程将看到在先前释放writeLock
所做的所有更新
以下内容来自维基百科:
读写锁是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁。[[1]](https://zh.wikipedia.org/wiki/%E8%AF%BB%E5%86%99%E9%94%81#cite_note-Hamilton-1)多读者锁,[2],“push lock”[3]) 用于解决读写问题。读操作可并发重入,写操作是互斥的。
线程池相关
Executor
一个Executor应该能执行被提交的Runnable方法,并且这个接口将任务提交和任务执行之间解耦了
实际上,许多Executor
的实现类会对任务执行进行调度,并将任务序列化后交给第二个executor
,如下所示
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;
public class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
@Override
public synchronized void execute(final Runnable r) {
tasks.offer(new Runnable() {
@Override
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}
Executors
总的来说这个类是线程池的工厂类,提供了多种线程池的生成静态方法
ExecutorService
-
ExecutorService
继承了Executor
接口,并且提供了shutdown()
方法来关闭线程池 -
Executor
只能接受Runnable
任务,而ExecutorService
还能接受Callable
任务 -
Executor
的submit()
方法没有返回值,ExecutorService
中提供了Future
型的返回值
Future
Future
代表一个异步计算的结果,并且提供了对应的方法来判断任务是完成还是取消了,也提供了等待任务完成的方法。
* interface ArchiveSearcher { String search(String target); }
* class App {
* ExecutorService executor = ...
* ArchiveSearcher searcher = ...
* void showSearch(final String target)
* throws InterruptedException {
* Future<String> future
* = executor.submit(new Callable<String>() {
* public String call() {
* return searcher.search(target);
* }});
* displayOtherThings(); // do other things while searching
* try {
* displayText(future.get()); // use future
* } catch (ExecutionException ex) { cleanup(); return; }
* }
* }}
网友评论