并发编程重要吗?当然重要,因为并发在我们的项目中真实存在,如果你不能充分了解它那么很可能造成严重的生产事故。最近隔壁项目组出了一个问题,每次请求接口之后都发现线程固定增加了5个,而且线程数一直增加没有减少,他们怀疑是中间件的问题,但实际上是因为他们的代码中线程池使用不当造成。所以了解并发是很有必要的。
并发编程由浅及深
- 1.基础概念
- 2.Thread
- 3.ThreadLocal(源码)
- 4.ReentrantLock(非公平锁实现源码)
- 5.ArrayBlockingQueue
- 6.CountDownLatch
- 7.ThreadPoolExecutor
1.基础概念
- 并行:两台饮水机,两个人去接水一人使用一台
- 并发:一台饮水机,两个人去接水,需要等待一个人接完才行
- 异步:不需要等待结果返回
- 同步:需要等待结果返回
- 线程:是运行在进程内:比如音乐播放器打开之后,在随机播放着音乐 的同时,还可以继续搜索我们想听的音乐。可以理解有两个线程分别处理着音乐播放和音乐搜索
- 进程:是一个具体的应用示例:比如打开音乐播放器就是运行了一个进程
- 线程上下文切换:cpu调度线程时切换线程同时需要保存线程相关数据,cpu和内存之间
2.Thread
2.1 线程的创建
线程的创建到底有几种?这个答案其实不是固定。要看回答的方向
There are two ways to create a new thread of execution
eg1:new Thread().start()
eg2:R implements Runnable
上面这句话是Thread类上面的注释,也就是官方说是两种,但是实际使用过程中根据根据这两种又有衍生和变化。概括有一下几种
1. new Thread().start();
2. R implements Runnable
new Thread(R).start();
3. C implements Callable
new Thread(new FutureTask<>(C)).start();
4. R implements Runnable
new ThreadPoolExecutor().execute(R);
2.2 线程的中断方法
1. Thread.interrupt() 给线程加一个中断标记
2. Thread.interrupted() 判断线程是否中断,会清除标记
3. Thread.isInterrupted() 判断线程是否中断,不会清除标记
2.3 线程的等待唤醒机制
1. synchronized 的 wait(),notify()
2. locksupport 的 park(),unpark()
3. condition wait() signal()
3.ThreadLocal(源码)
下面这段代码大概就是我们使用ThreadLocal的方式,接下来将根据这段代码中的方法对ThreadLocal原理进行剖析。
// 1.new
ThreadLocal<Map<String,String>> myThreadLocal = new ThreadLocal<>();
// 2.存值
Map<String,String> map = new HashMap<>();
map.put("org","0001");
myThreadLocal.set(map);
// 3.获取值
map = myThreadLocal.get();
String org = map.get("org");
System.out.println(org);
// 4.清空
myThreadLocal.remove();
3.1 new ThreadLocal<>()
public ThreadLocal() {
}
3.2 ThreadLocal#set()
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取thread对应的map
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// 创建map
createMap(t, value);
}
3.2.1ThreadLocal#createMap
void createMap(Thread t, T firstValue) {
// 给thread的threadLocals属性赋值
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
3.2.1.1 new ThreadLocalMap()
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
// 创建初始长度为16的数组
table = new Entry[INITIAL_CAPACITY];
// firstKey传的是this 即myThreadLocal对象本身
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 数组中存入值:key=myThreadLocal,firstValue=map
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
3.2.1.2 ThreadLocal#getMap
ThreadLocalMap getMap(Thread t) {
// 获取thread中的ThreadLocalMap属性
return t.threadLocals;
}
执行完myThreadLocal.set(map),最终结构如下图:
3.3 myThreadLocal.get() 方法
public T get() {
Thread t = Thread.currentThread();
// 获取thread的属性ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 获取key为myThreadLocal的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
// 返回value即为set存储的map
return result;
}
}
// 如果当前线程未set值,默认返回null
return setInitialValue();
}
3.3.1 map.getEntry(this) 方法
private Entry getEntry(ThreadLocal<?> key) {
// 和set时一样,使用myThreadLocal对象获取数组的下标
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
// 当前下标位置数组值不为null,且key值相等。即为get返回值
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
3.4 myThreadLocal.remove() 方法
public void remove() {
// 获取当前程的属性ThreadLocalMap
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
// 移除当前myThreadLocal对象所在下标的数组的值
m.remove(this);
}
3.4.1 m.remove(this)
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
// 获取对象下标的数组值
for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
4.ReentrantLock(非公平锁实现源码)
下面的源码实现主要列的是非公平锁,公平锁代码和非公平锁差不多所以鼓励大家自己看看。看源码前请大家想象一个场景,现在有两个线程,第一个线程获取锁成功了在执行业务逻辑时,此时第二个线程过来加锁。下面的源码分析的就是上面这样一个场景。
常规使用如下:
// 1.创建 ReentrantLock 对象
ReentrantLock lock = new ReentrantLock();
// 2.获取锁
lock.lock();
try {
// 业务逻辑
} finally {
// 3.释放锁
lock.unlock();
}
4.1 new ReentrantLock()
public ReentrantLock() {
// 给ReentrantLock属性赋值;NonfairSync实际上就是抽象的队列同步器
sync = new NonfairSync();
}
NonfairSync类的继承关系
4.2 lock.lock()
public void lock() {
sync.lock();
}
4.2.1 sync.lock()
final void lock() {
// 使用CAS的方式尝试将同步等待队列中的state由0改为1
if (compareAndSetState(0, 1))
// 修改成功代表获取锁成功,将独占线程赋值为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 1.获取锁
acquire(1);
}
4.2.1.1 acquire(1)
public final void acquire(int arg) {
// tryAcquire尝试获取一次锁,如果失败了addWaiter进入队列,然后再次获取锁acquireQueued
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
4.2.1.1.1 tryAcquire
protected final boolean tryAcquire(int acquires) {
// 非公平获取锁
return nonfairTryAcquire(acquires);
}
4.2.1.1.1.1 nonfairTryAcquire()方法
// acquires从2.1方法中传过来 固定为1
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取同步等待队列的state属性,默认值为0,如果不为0代表有线程获取锁成功
int c = getState();
if (c == 0) {
// 如果当前没有线程获取到锁,就直接cas加锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// c!=0,当前已有线程获取了锁。判断是不是同一个线程
else if (current == getExclusiveOwnerThread()) {
// 可重入锁实现 通过state值自增判断重入几次
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
4.2.1.1.2 addWaiter
// node从4.2.1.1传递的是null
private Node addWaiter(Node mode) {
// 构建node节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 1.将尾节点赋值给变量pred
Node pred = tail;
// 2.判断尾节点是否为空,为空则代表链表为空未进行初始化(双向指针构造的)
if (pred != null) {
// 2.1 尾节点不为空,则将当前node节点添加到链表尾部
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next =